diff -r 3c013ef88a00 -r 475c8b8df67f src/EditRecipeTab2.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/EditRecipeTab2.cpp Mon Apr 11 17:22:43 2022 +0200 @@ -0,0 +1,901 @@ +/** + * EditRecipe.cpp is part of bmsapp. + * + * Tab 2, fermetables + * + * bmsapp is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * bmsapp is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + + + +bool EditRecipe::ferment_sort_test(const Fermentables &D1, const Fermentables &D2) +{ + return (D1.f_added <= D2.f_added) && (D1.f_amount >= D2.f_amount) && (D1.f_color < D2.f_color); +} + + +void EditRecipe::to100Fermentables(int row) +{ + if (recipe->fermentables.at(row).f_adjust_to_total_100) { + QWidget *pWidget = new QWidget(); + QLabel *label = new QLabel; + label->setPixmap(QPixmap(":icons/silk/tick.png")); + QHBoxLayout *pLayout = new QHBoxLayout(pWidget); + pLayout->addWidget(label); + pLayout->setAlignment(Qt::AlignCenter); + pLayout->setContentsMargins(0, 0, 0, 0); + pWidget->setLayout(pLayout); + ui->fermentablesTable->setCellWidget(row, 9, pWidget); + } else { + ui->fermentablesTable->removeCellWidget(row, 9); + } +} + + +void EditRecipe::refreshFermentables() +{ + QString w; + QWidget* pWidget; + QHBoxLayout* pLayout; + QTableWidgetItem *item; + + qDebug() << "refreshFermentables" << recipe->fermentables.size(); + std::sort(recipe->fermentables.begin(), recipe->fermentables.end(), ferment_sort_test); + + /* + * During filling the table turn off the cellChanged signal because every cell that is filled + * triggers the cellChanged signal. The QTableWidget has no better signal to use. + */ + this->ignoreChanges = true; + + const QStringList labels({tr("Supplier"), tr("Fermentable"), tr("EBC"), tr("Type"), tr("Graintype"), tr("When"), tr("Yield"), + tr("Amount"), tr("Procent"), tr("100%"), tr("Delete"), tr("Edit") }); + ui->fermentablesTable->setColumnCount(12); + ui->fermentablesTable->setColumnWidth(0, 150); /* Supplier */ + ui->fermentablesTable->setColumnWidth(1, 225); /* Fermentable */ + ui->fermentablesTable->setColumnWidth(2, 50); /* Color */ + ui->fermentablesTable->setColumnWidth(3, 75); /* Type */ + ui->fermentablesTable->setColumnWidth(4, 75); /* Graintype */ + ui->fermentablesTable->setColumnWidth(5, 82); /* Added */ + ui->fermentablesTable->setColumnWidth(6, 60); /* Yield */ + ui->fermentablesTable->setColumnWidth(7, 90); /* Amount */ + ui->fermentablesTable->setColumnWidth(8, 60); /* Procent */ + ui->fermentablesTable->setColumnWidth(9, 50); /* 100% */ + ui->fermentablesTable->setColumnWidth(10, 80); /* Delete */ + ui->fermentablesTable->setColumnWidth(11, 80); /* Edit */ + ui->fermentablesTable->setHorizontalHeaderLabels(labels); + ui->fermentablesTable->verticalHeader()->hide(); + ui->fermentablesTable->setRowCount(recipe->fermentables.size()); + + for (int i = 0; i < recipe->fermentables.size(); i++) { + + ui->fermentablesTable->setItem(i, 0, new QTableWidgetItem(recipe->fermentables.at(i).f_supplier)); + ui->fermentablesTable->setItem(i, 1, new QTableWidgetItem(recipe->fermentables.at(i).f_name)); + + w = QString("%1").arg(recipe->fermentables.at(i).f_color, 1, 'f', 0, '0'); + item = new QTableWidgetItem(w); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->fermentablesTable->setItem(i, 2, item); + + item = new QTableWidgetItem(f_types[recipe->fermentables.at(i).f_type]); + item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); + ui->fermentablesTable->setItem(i, 3, item); + + item = new QTableWidgetItem(f_graintypes[recipe->fermentables.at(i).f_graintype]); + item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); + ui->fermentablesTable->setItem(i, 4, item); + + item = new QTableWidgetItem(f_added[recipe->fermentables.at(i).f_added]); + item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); + ui->fermentablesTable->setItem(i, 5, item); + + item = new QTableWidgetItem(QString("%1%").arg(recipe->fermentables.at(i).f_yield, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->fermentablesTable->setItem(i, 6, item); + + item = new QTableWidgetItem(QString("%1 Kg").arg(recipe->fermentables.at(i).f_amount, 4, 'f', 3, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->fermentablesTable->setItem(i, 7, item); + + if (recipe->fermentables.at(i).f_added < 4) { + item = new QTableWidgetItem(QString("%1%").arg(recipe->fermentables.at(i).f_percentage, 2, 'f', 1, '0')); + } else { + item = new QTableWidgetItem(QString("")); // Blank for bottling and kegging. + } + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->fermentablesTable->setItem(i, 8, item); + + to100Fermentables(i); + + /* Add the Delete row button */ + pWidget = new QWidget(); + QPushButton* btn_dele = new QPushButton(); + btn_dele->setObjectName(QString("%1").arg(i)); /* Send row with the button */ + btn_dele->setText(tr("Delete")); + connect(btn_dele, SIGNAL(clicked()), this, SLOT(on_deleteFermentRow_clicked())); + pLayout = new QHBoxLayout(pWidget); + pLayout->addWidget(btn_dele); + pLayout->setContentsMargins(5, 0, 5, 0); + pWidget->setLayout(pLayout); + ui->fermentablesTable->setCellWidget(i, 10, pWidget); + + pWidget = new QWidget(); + QPushButton* btn_edit = new QPushButton(); + btn_edit->setObjectName(QString("%1").arg(i)); /* Send row with the button */ + btn_edit->setText(tr("Edit")); + connect(btn_edit, SIGNAL(clicked()), this, SLOT(on_editFermentRow_clicked())); + pLayout = new QHBoxLayout(pWidget); + pLayout->addWidget(btn_edit); + pLayout->setContentsMargins(5, 0, 5, 0); + pWidget->setLayout(pLayout); + ui->fermentablesTable->setCellWidget(i, 11, pWidget); + } + this->ignoreChanges = false; +} + + +void EditRecipe::calcFermentables() +{ + int i; + double psugar = 0, pcara = 0, d, s = 0, x, color; + double vol = 0; // Volume sugars after boil + double addedS = 0; // Added sugars after boil + double addedmass = 0; // Added mass after boil + double mvol = 0; // Mash volume + double lintner = 0; // Total recipe lintner + double mashkg = 0; + double sugarsf = 0; // fermentable sugars mash + boil + double sugarsm = 0; // fermentable sugars in mash + double sugardensity = 1.611; // kg/l in solution + double mashtime = 0; // Total mash time + double mashtemp = 0; // Average mash temperature + double mashinfuse = 0; // Mash infuse amount + double colort = 0; // Colors srm * vol totals + double colorh = 0; // Colors ebc * vol * kt + double colorn = 0; // Colors ebc * pt * pct + + qDebug() << "calcFermentables()"; + + /* + * Get average mashtemp and mashtime from the Mash schedule. + * It is possible that the schedule is not (yet) present. + */ + if (recipe->mashs.size() > 0) { + for (i = 0; i < recipe->mashs.size(); i++) { + if (recipe->mashs.at(i).step_type == 0) // Infusion + mashinfuse += recipe->mashs.at(i).step_infuse_amount; + if (recipe->mashs.at(i).step_temp < 75) { // Ignore mashout + mashtime += recipe->mashs.at(i).step_time; + mashtemp += recipe->mashs.at(i).step_time * recipe->mashs.at(i).step_temp; + } + } + mashtemp = mashtemp / mashtime; + mvol = mashinfuse; + qDebug() << " mash time" << mashtime << "temp" << mashtemp << "infuse" << mashinfuse; + } else { + qDebug() << " no mash schedule"; + } + + if (recipe->fermentables.size() < 1) { + qDebug() << " no fermentables, return."; + recipe->est_og = 0.980; + ui->est_ogEdit->setValue(0.980); + ui->est_og2Edit->setValue(0.980); + ui->est_og3Edit->setValue(0.980); + ui->est_ogShow->setValue(0.980); + recipe->est_color = 0; + ui->est_colorEdit->setValue(0); + ui->est_colorEdit->setStyleSheet(Utils::ebc_to_style(0)); + ui->est_color2Edit->setValue(0); + ui->est_color2Edit->setStyleSheet(Utils::ebc_to_style(0)); + ui->est_colorShow->setValue(0); + ui->perc_mashShow->setValue(0); + ui->perc_sugarsShow->setValue(0); + ui->perc_caraShow->setValue(0); + ui->lintnerShow->setValue(0); + recipe->est_fg = 0.980; + ui->est_fgEdit->setValue(0.980); + ui->est_fg3Edit->setValue(0.980); + ui->est_fgShow->setValue(0.980); + ui->est_abvEdit->setValue(0); + ui->est_abv2Edit->setValue(0); + ui->est_abvShow->setValue(0); + recipe->est_abv = 0; + ui->calEdit->setValue(0); + return; + } + qDebug() << " adjust to 100" << recipe->fermentables_use100; + + for (i = 0; i < recipe->fermentables.size(); i++) { + if (recipe->fermentables.at(i).f_type == 1 && recipe->fermentables.at(i).f_added < 4) // Sugars + psugar += recipe->fermentables.at(i).f_percentage; + if (recipe->fermentables.at(i).f_graintype == 2 && recipe->fermentables.at(i).f_added < 4) // Crystal/Cara + pcara += recipe->fermentables.at(i).f_percentage; + d = recipe->fermentables.at(i).f_amount * (recipe->fermentables.at(i).f_yield / 100) * (1 - recipe->fermentables.at(i).f_moisture / 100); + if (recipe->fermentables.at(i).f_added == 0) { // Mash + if (mvol > 0) { // If mash volume is known + mvol += recipe->fermentables.at(i).f_amount * recipe->fermentables.at(i).f_moisture / 100; + s += d; + } + d = ui->efficiencyEdit->value() / 100 * d; + sugarsm += d; + mashkg += recipe->fermentables.at(i).f_amount; + } + if (recipe->fermentables.at(i).f_added == 0 || recipe->fermentables.at(i).f_added == 1) // Mash or boil + sugarsf += d; + if (recipe->fermentables.at(i).f_added == 2 || recipe->fermentables.at(i).f_added == 3) { // Fermentation or lagering + x = (recipe->fermentables.at(i).f_yield / 100) * (1 - recipe->fermentables.at(i).f_moisture / 100); + addedS += recipe->fermentables.at(i).f_amount * x; + addedmass += recipe->fermentables.at(i).f_amount; + vol += (x * sugardensity + (1 - x) * 1) * recipe->fermentables.at(i).f_amount; + } + if (recipe->fermentables.at(i).f_added == 0 && + (recipe->fermentables.at(i).f_type == 0 || recipe->fermentables.at(i).f_type == 4) && + recipe->fermentables.at(i).f_color < 50) { + lintner += recipe->fermentables.at(i).f_diastatic_power * recipe->fermentables.at(i).f_amount; + } + if (recipe->fermentables.at(i).f_added < 4) { + colort += recipe->fermentables.at(i).f_amount * Utils::ebc_to_srm(recipe->fermentables.at(i).f_color); + colorh += recipe->fermentables.at(i).f_amount * recipe->fermentables.at(i).f_color * Utils::get_kt(recipe->fermentables.at(i).f_color); + colorn += (recipe->fermentables.at(i).f_percentage / 100) * recipe->fermentables.at(i).f_color; // For 8.6 Pt wort. + } + } + qDebug() << " colort" << colort << "colorh" << colorh << "colorn" << colorn; + qDebug() << " psugar" << psugar << "pcara" << pcara << "mvol" << mvol; + qDebug() << " sugarsf" << sugarsf << "sugarsm" << sugarsm; + + double og = Utils::estimate_sg(sugarsf + addedS, ui->batch_sizeEdit->value()); + qDebug() << " OG" << ui->est_ogEdit->value() << og; + recipe->est_og = og; + ui->est_ogEdit->setValue(og); + ui->est_og2Edit->setValue(og); + ui->est_og3Edit->setValue(og); + ui->est_ogShow->setValue(og); + + recipe->preboil_sg = Utils::estimate_sg(sugarsm, ui->boil_sizeEdit->value()); + qDebug() << " preboil SG" << recipe->preboil_sg; + + /* + * Color of the wort + */ + if (ui->color_methodEdit->currentIndex() == 4) { // Naudts + color = round(((Utils::sg_to_plato(og) / 8.6) * colorn) + (ui->boil_timeEdit->value() / 60)); + } else if (ui->color_methodEdit->currentIndex() == 3) { // Hans Halberstadt + double bv = 0.925; // Beer loss efficiency + double sr = 0.95; // Mash and sparge efficiency + color = round((4.46 * bv * sr) / ui->batch_sizeEdit->value() * colorh); + } else { + double cw = colort / ui->batch_sizeEdit->value() * 8.34436; + color = Utils::kw_to_ebc(ui->color_methodEdit->currentIndex(), cw); + } + qDebug() << " color" << ui->est_colorEdit->value() << color << recipe->est_color; + recipe->est_color = color; + ui->est_colorEdit->setValue(color); + ui->est_colorEdit->setStyleSheet(Utils::ebc_to_style(color)); + ui->est_color2Edit->setValue(color); + ui->est_color2Edit->setStyleSheet(Utils::ebc_to_style(color)); + ui->est_colorShow->setValue(color); + + /* + * We don't have a equipment profile in recipes, + * so we assume a certain guessed mashtun size. + */ + ui->perc_mashShow->setValue(round(mashkg / (ui->boil_sizeEdit->value() / 3) * 100)); + ui->perc_sugarsShow->setValue(round(psugar)); + ui->perc_caraShow->setValue(round(pcara)); + qDebug() << " lintner" << lintner << " mashkg" << mashkg << "final" << round(lintner / mashkg); + ui->lintnerShow->setValue(round(lintner / mashkg)); + + /* + * Calculate the apparant attenuation. + */ + double svg = 0; + if (recipe->yeasts.size() > 0) { + for (i = 0; i < recipe->yeasts.size(); i++) { + if (recipe->yeasts.at(i).y_use == 0) { // Used in primary + if (recipe->yeasts.at(i).y_attenuation > svg) + svg = recipe->yeasts.at(i).y_attenuation; // Take the highest if multiple yeasts. + } + // TODO: brett or others in secondary. + } + qDebug() << " SVG" << svg; + } + if (svg == 0) + svg = 77.0; + + double fg; + if (mashkg > 0 && mashinfuse > 0 && mashtime > 0 && mashtemp > 0) + fg = Utils::estimate_fg(psugar, pcara, mashinfuse / mashkg, mashtime, mashtemp, svg, og); + else + fg = Utils::estimate_fg(psugar, pcara, 0, 0, 0, svg, og); + qDebug() << " FG" << ui->est_fgEdit->value() << fg; + recipe->est_fg = fg; + ui->est_fgEdit->setValue(fg); + ui->est_fg3Edit->setValue(fg); + ui->est_fgShow->setValue(fg); + + double abv = Utils::abvol(og, fg); + qDebug() << " ABV" << ui->est_abvEdit->value() << abv; + ui->est_abvEdit->setValue(abv); + ui->est_abv2Edit->setValue(abv); + ui->est_abvShow->setValue(abv); + recipe->est_abv = abv; + + /* + * Calculate kilocalories/liter. Formula from brouwhulp. + * Take the alcohol and sugar parts and then combine. + */ + double alc = 1881.22 * fg * (og - fg) / (1.775 - og); + double sug = 3550 * fg * (0.1808 * og + 0.8192 * fg - 1.0004); + ui->calEdit->setValue(round((alc + sug) / (12 * 0.0295735296))); +} + + +void EditRecipe::on_perc_mash_valueChanged(int value) +{ + if (value < 90) + ui->perc_mashShow->setStyleSheet(bar_green); + else if (value < 100) + ui->perc_mashShow->setStyleSheet(bar_orange); + else + ui->perc_mashShow->setStyleSheet(bar_red); +} + + +void EditRecipe::on_perc_sugars_valueChanged(int value) +{ + if (value < 20) + ui->perc_sugarsShow->setStyleSheet(bar_green); + else + ui->perc_sugarsShow->setStyleSheet(bar_red); +} + + +void EditRecipe::on_perc_cara_valueChanged(int value) +{ + if (value < 25) + ui->perc_caraShow->setStyleSheet(bar_green); + else + ui->perc_caraShow->setStyleSheet(bar_red); +} + + +void EditRecipe::on_lintner_valueChanged(int value) +{ + if (value < 30) + ui->lintnerShow->setStyleSheet(bar_red); + else if (value < 40) + ui->lintnerShow->setStyleSheet(bar_orange); + else + ui->lintnerShow->setStyleSheet(bar_green); +} + + +void EditRecipe::on_addFermentRow_clicked() +{ + Fermentables newf; + + qDebug() << "Add fermentable row"; + + for (int i = 0; i < recipe->fermentables.size(); i++) { + if (recipe->fermentables.at(i).f_amount == 0 && recipe->fermentables.at(i).f_color == 0) + return; // Add only one at a time. + } + + newf.f_name = "Select one"; + newf.f_origin = ""; + newf.f_supplier = ""; + newf.f_amount = 0; + newf.f_cost = 0; + newf.f_type = 0; + newf.f_yield = 0; + newf.f_color = 0; + newf.f_coarse_fine_diff = 0; + newf.f_moisture = 0; + newf.f_diastatic_power = 0; + newf.f_protein = 0; + newf.f_dissolved_protein = 0; + newf.f_max_in_batch = 100; + newf.f_graintype = 0; + newf.f_added = 0; + newf.f_recommend_mash = true; + newf.f_add_after_boil = false; + newf.f_adjust_to_total_100 = false; + newf.f_percentage = 0; + newf.f_di_ph = 0; + newf.f_acid_to_ph_57 = 0; + + recipe->fermentables.append(newf); + emit refreshAll(); +} + + +void EditRecipe::on_deleteFermentRow_clicked() +{ + QPushButton *pb = qobject_cast(QObject::sender()); + int row = pb->objectName().toInt(); + qDebug() << "Delete fermentable row" << row << recipe->fermentables.size(); + + if (recipe->fermentables.size() < 1) + return; + + int rc = QMessageBox::warning(this, tr("Delete fermentable"), tr("Delete %1").arg(recipe->fermentables.at(row).f_name), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No); + if (rc == QMessageBox::No) + return; + + this->ignoreChanges = true; + recipe->fermentables.removeAt(row); + + /* + * Recalculate the percentages on the rows left. + */ + double total = 0; + for (int i = 0; i < recipe->fermentables.size(); i++) + if (recipe->fermentables.at(i).f_added < 4) // Only before bottle/kegging + total += recipe->fermentables.at(i).f_amount; + for (int i = 0; i < recipe->fermentables.size(); i++) + if (recipe->fermentables.at(i).f_added < 4) + recipe->fermentables[i].f_percentage = recipe->fermentables.at(i).f_amount / total * 100; + + this->ignoreChanges = false; + emit refreshAll(); +} + + +void EditRecipe::ferment_amount_changed(double val) +{ + QTableWidgetItem *item; + double total = 0, perc; + + if (recipe->fermentables_use100) + return; + + qDebug() << "ferment_amount_changed()" << recipe->fermentables_row << val; + this->ignoreChanges = true; + + recipe->fermentables[recipe->fermentables_row].f_amount = val; + item = new QTableWidgetItem(QString("%1 Kg").arg(val, 4, 'f', 3, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->fermentablesTable->setItem(recipe->fermentables_row, 7, item); + + for (int i = 0; i < recipe->fermentables.size(); i++) + if (recipe->fermentables.at(i).f_added < 4) // Only before bottle/kegging + total += recipe->fermentables.at(i).f_amount; + /* + * Recalculate the percentages + */ + for (int i = 0; i < recipe->fermentables.size(); i++) { + if (recipe->fermentables.at(i).f_added < 4) { + perc = recipe->fermentables.at(i).f_amount / total * 100; + recipe->fermentables[i].f_percentage = perc; + item = new QTableWidgetItem(QString("%1%").arg(perc, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->fermentablesTable->setItem(i, 8, item); + if (i == recipe->fermentables_row) + this->pctEdit->setValue(perc); + } + } + this->ignoreChanges = false; + is_changed(); +} + + +void EditRecipe::ferment_pct_changed(double val) +{ + QTableWidgetItem *item; + double total = 0, row100 = -1; + + if (! recipe->fermentables_use100) + return; + + qDebug() << "ferment_pct_changed()" << recipe->fermentables_row << val; + /* + * Since we have arrived here, adjust_to_100 is active and + * this is not the entry to be adjusted to 100. + */ + for (int i = 0; i < recipe->fermentables.size(); i++) { + if (recipe->fermentables.at(i).f_added < 4) // Only before bottle/kegging + total += recipe->fermentables.at(i).f_amount; + if (recipe->fermentables.at(i).f_adjust_to_total_100) + row100 = i; + } + double oldperc = recipe->fermentables.at(recipe->fermentables_row).f_percentage; + double diffp = val - oldperc; + double diffw = (diffp / 100) * total; + qDebug() << "row100" << row100 << "total" << total << "diff kg" << diffw << "diff %" << diffp; + + this->ignoreChanges = true; + recipe->fermentables[recipe->fermentables_row].f_percentage += diffp; + recipe->fermentables[recipe->fermentables_row].f_amount += diffw; + recipe->fermentables[row100].f_percentage -= diffp; + recipe->fermentables[row100].f_amount -= diffw; + + item = new QTableWidgetItem(QString("%1 Kg").arg(recipe->fermentables[recipe->fermentables_row].f_amount, 4, 'f', 3, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->fermentablesTable->setItem(recipe->fermentables_row, 7, item); + this->famountEdit->setValue(recipe->fermentables[recipe->fermentables_row].f_amount); + + item = new QTableWidgetItem(QString("%1%").arg(recipe->fermentables[recipe->fermentables_row].f_percentage, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->fermentablesTable->setItem(recipe->fermentables_row, 8, item); + + item = new QTableWidgetItem(QString("%1 Kg").arg(recipe->fermentables[row100].f_amount, 4, 'f', 3, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->fermentablesTable->setItem(row100, 7, item); + + item = new QTableWidgetItem(QString("%1%").arg(recipe->fermentables[row100].f_percentage, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->fermentablesTable->setItem(row100, 8, item); + + this->ignoreChanges = false; + is_changed(); +} + + +void EditRecipe::ferment_to100_changed(bool val) +{ + qDebug() << "ferment_to100_changed()" << recipe->fermentables_row << val << recipe->fermentables_use100; + + /* + * Three scenario's. + * 1. There is no fermentable selected yet, just mark it. + * 2. There is current one is selected, deselect it. + * 3. There is another one selected, deselect and select this one. + */ + if (! recipe->fermentables_use100 && val) { + /* Scenario 1. */ + recipe->fermentables_use100 = true; + recipe->fermentables[recipe->fermentables_row].f_adjust_to_total_100 = true; + pctEdit->setReadOnly(false); + famountEdit->setReadOnly(true); + } else if (recipe->fermentables_use100 && recipe->fermentables[recipe->fermentables_row].f_adjust_to_total_100 && ! val) { + /* Scenario 2. */ + recipe->fermentables[recipe->fermentables_row].f_adjust_to_total_100 = false; + recipe->fermentables_use100 = false; + pctEdit->setReadOnly(true); + famountEdit->setReadOnly(false); + } else if (recipe->fermentables_use100 && ! recipe->fermentables[recipe->fermentables_row].f_adjust_to_total_100 && val) { + /* Scenario 3. */ + for (int i = 0; i < recipe->fermentables.size(); i++) { + recipe->fermentables[i].f_adjust_to_total_100 = false; + } + recipe->fermentables[recipe->fermentables_row].f_adjust_to_total_100 = true; + } else { + qDebug() << "bug"; + return; + } + + this->ignoreChanges = true; + for (int i = 0; i < recipe->fermentables.size(); i++) { + to100Fermentables(i); + } + this->ignoreChanges = false; + is_changed(); +} + +void EditRecipe::ferment_select_changed(int val) +{ + QSqlQuery query; + bool instock = finstockEdit->isChecked(); + QString w; + QTableWidgetItem *item; + + if (val < 1) + return; + + qDebug() << "ferment_select_changed()" << recipe->fermentables_row << val << instock; + + /* + * Search the fermentable pointed by the index and instock flag. + */ + QString sql = "SELECT name,origin,supplier,cost,type,yield,color,coarse_fine_diff,moisture,diastatic_power,protein,dissolved_protein,max_in_batch," + "graintype,recommend_mash,add_after_boil,di_ph,acid_to_ph_57 FROM inventory_fermentables "; + if (instock) + sql.append("WHERE inventory > 0 "); + sql.append("ORDER BY supplier,name"); + query.prepare(sql); + query.exec(); + query.first(); + for (int i = 0; i < (val - 1); i++) { + query.next(); + } + qDebug() << "found" << query.value(2).toString() << query.value(0).toString(); + + /* + * Replace the fermentable record contents + */ + this->ignoreChanges = true; + recipe->fermentables[recipe->fermentables_row].f_name = query.value(0).toString(); + recipe->fermentables[recipe->fermentables_row].f_origin = query.value(1).toString(); + recipe->fermentables[recipe->fermentables_row].f_supplier = query.value(2).toString(); + recipe->fermentables[recipe->fermentables_row].f_cost = query.value(3).toDouble(); + recipe->fermentables[recipe->fermentables_row].f_type = query.value(4).toInt(); + recipe->fermentables[recipe->fermentables_row].f_yield = query.value(5).toDouble(); + recipe->fermentables[recipe->fermentables_row].f_color = query.value(6).toDouble(); + recipe->fermentables[recipe->fermentables_row].f_coarse_fine_diff = query.value(7).toDouble(); + recipe->fermentables[recipe->fermentables_row].f_moisture = query.value(8).toDouble(); + recipe->fermentables[recipe->fermentables_row].f_diastatic_power = query.value(9).toDouble(); + recipe->fermentables[recipe->fermentables_row].f_protein = query.value(10).toDouble(); + recipe->fermentables[recipe->fermentables_row].f_dissolved_protein = query.value(11).toDouble(); + recipe->fermentables[recipe->fermentables_row].f_max_in_batch = query.value(12).toDouble(); + recipe->fermentables[recipe->fermentables_row].f_graintype = query.value(13).toInt(); + recipe->fermentables[recipe->fermentables_row].f_recommend_mash = query.value(14).toInt() ? true:false; + recipe->fermentables[recipe->fermentables_row].f_add_after_boil = query.value(15).toInt() ? true:false; + recipe->fermentables[recipe->fermentables_row].f_di_ph = query.value(16).toDouble(); + recipe->fermentables[recipe->fermentables_row].f_acid_to_ph_57 = query.value(17).toDouble(); + + /* + * Update the visible fields + */ + fnameEdit->setText(recipe->fermentables.at(recipe->fermentables_row).f_name); + fsupplierEdit->setText(recipe->fermentables.at(recipe->fermentables_row).f_supplier); + fmaxEdit->setValue(recipe->fermentables.at(recipe->fermentables_row).f_max_in_batch); + + ui->fermentablesTable->setItem(recipe->fermentables_row, 0, new QTableWidgetItem(recipe->fermentables.at(recipe->fermentables_row).f_supplier)); + ui->fermentablesTable->setItem(recipe->fermentables_row, 1, new QTableWidgetItem(recipe->fermentables.at(recipe->fermentables_row).f_name)); + + w = QString("%1").arg(recipe->fermentables.at(recipe->fermentables_row).f_color, 1, 'f', 0, '0'); + item = new QTableWidgetItem(w); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->fermentablesTable->setItem(recipe->fermentables_row, 2, item); + + item = new QTableWidgetItem(f_types[recipe->fermentables.at(recipe->fermentables_row).f_type]); + item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); + ui->fermentablesTable->setItem(recipe->fermentables_row, 3, item); + + item = new QTableWidgetItem(f_graintypes[recipe->fermentables.at(recipe->fermentables_row).f_graintype]); + item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); + ui->fermentablesTable->setItem(recipe->fermentables_row, 4, item); + + item = new QTableWidgetItem(QString("%1%").arg(recipe->fermentables.at(recipe->fermentables_row).f_yield, 2, 'f', 1, '0')); + item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter); + ui->fermentablesTable->setItem(recipe->fermentables_row, 6, item); + + this->ignoreChanges = false; + calcFermentables(); + is_changed(); +} + + +void EditRecipe::ferment_instock_changed(bool val) +{ + QSqlQuery query; + + qDebug() << "ferment_instock_changed()" << recipe->fermentables_row << val; + + this->fselectEdit->setCurrentIndex(-1); + this->fselectEdit->clear(); + QString sql = "SELECT supplier,name,color,inventory FROM inventory_fermentables "; + if (val) + sql.append("WHERE inventory > 0 "); + sql.append("ORDER BY supplier,name"); + query.prepare(sql); + query.exec(); + query.first(); + this->fselectEdit->addItem(""); // Start with empty value + for (int i = 0; i < query.size(); i++) { + this->fselectEdit->addItem(query.value(0).toString()+" - "+query.value(1).toString()+" ("+query.value(2).toString()+" EBC) "+ + QString("%1 kg").arg(query.value(3).toDouble(), 4, 'f', 3, '0')); + query.next(); + } +} + + +void EditRecipe::ferment_added_changed(int val) +{ + qDebug() << "ferment_added_changed()" << recipe->fermentables_row << val; + + this->ignoreChanges = true; + recipe->fermentables[recipe->fermentables_row].f_added = val; + QTableWidgetItem *item = new QTableWidgetItem(f_added[val]); + item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter); + ui->fermentablesTable->setItem(recipe->fermentables_row, 5, item); + + double total = 0; + for (int i = 0; i < recipe->fermentables.size(); i++) + if (recipe->fermentables.at(i).f_added < 4) // Only before bottle/kegging + total += recipe->fermentables.at(i).f_amount; + for (int i = 0; i < recipe->fermentables.size(); i++) + if (recipe->fermentables.at(i).f_added < 4) + recipe->fermentables[i].f_percentage = recipe->fermentables.at(i).f_amount / total * 100; + + this->ignoreChanges = false; + is_changed(); + emit refreshAll(); +} + + +void EditRecipe::on_editFermentRow_clicked() +{ + QSqlQuery query; + + QPushButton *pb = qobject_cast(QObject::sender()); + recipe->fermentables_row = pb->objectName().toInt(); + qDebug() << "Edit fermentable row" << recipe->fermentables_row; + Fermentables backup = recipe->fermentables.at(recipe->fermentables_row); + + QDialog* dialog = new QDialog(this); + dialog->resize(738, 287); + QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog); + buttonBox->setObjectName(QString::fromUtf8("buttonBox")); + buttonBox->setGeometry(QRect(30, 240, 671, 32)); + buttonBox->setLayoutDirection(Qt::LeftToRight); + buttonBox->setOrientation(Qt::Horizontal); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); + buttonBox->setCenterButtons(true); + QLabel *nameLabel = new QLabel(dialog); + nameLabel->setObjectName(QString::fromUtf8("nameLabel")); + nameLabel->setText(tr("Current ingredient:")); + nameLabel->setGeometry(QRect(10, 10, 141, 20)); + nameLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + QLabel *supplierLabel = new QLabel(dialog); + supplierLabel->setObjectName(QString::fromUtf8("supplierLabel")); + supplierLabel->setText(tr("Supplier:")); + supplierLabel->setGeometry(QRect(10, 40, 141, 20)); + supplierLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + QLabel *amountLabel = new QLabel(dialog); + amountLabel->setObjectName(QString::fromUtf8("amountLabel")); + amountLabel->setText(tr("Amount in kg:")); + amountLabel->setGeometry(QRect(10, 100, 141, 20)); + amountLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + QLabel *pctLabel = new QLabel(dialog); + pctLabel->setObjectName(QString::fromUtf8("pctLabel")); + pctLabel->setText(tr("Percentage in batch:")); + pctLabel->setGeometry(QRect(10, 130, 141, 20)); + pctLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + QLabel *to100Label = new QLabel(dialog); + to100Label->setObjectName(QString::fromUtf8("to100Label")); + to100Label->setText(tr("Auto fill to 100%:")); + to100Label->setGeometry(QRect(10, 160, 141, 20)); + to100Label->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + QLabel *addedLabel = new QLabel(dialog); + addedLabel->setObjectName(QString::fromUtf8("addedLabel")); + addedLabel->setText(tr("Use at:")); + addedLabel->setGeometry(QRect(10, 190, 141, 20)); + addedLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + QLabel *selectLabel = new QLabel(dialog); + selectLabel->setObjectName(QString::fromUtf8("selectLabel")); + selectLabel->setText(tr("Select ingredient:")); + selectLabel->setGeometry(QRect(10, 70, 141, 20)); + selectLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + QLabel *instockLabel = new QLabel(dialog); + instockLabel->setObjectName(QString::fromUtf8("instockLabel")); + instockLabel->setText(tr("In stock:")); + instockLabel->setGeometry(QRect(525, 70, 121, 20)); + instockLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + QLabel *maxLabel = new QLabel(dialog); + maxLabel->setObjectName(QString::fromUtf8("maxLabel")); + maxLabel->setText(tr("Max in batch:")); + maxLabel->setGeometry(QRect(420, 130, 121, 20)); + maxLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + fselectEdit = new QComboBox(dialog); + fselectEdit->setObjectName(QString::fromUtf8("fselectEdit")); + fselectEdit->setGeometry(QRect(160, 70, 371, 23)); + + fnameEdit = new QLineEdit(dialog); + fnameEdit->setObjectName(QString::fromUtf8("fnameEdit")); + fnameEdit->setText(recipe->fermentables.at(recipe->fermentables_row).f_name); + fnameEdit->setGeometry(QRect(160, 10, 511, 23)); + fnameEdit->setReadOnly(true); + fsupplierEdit = new QLineEdit(dialog); + fsupplierEdit->setObjectName(QString::fromUtf8("fsupplierEdit")); + fsupplierEdit->setText(recipe->fermentables.at(recipe->fermentables_row).f_supplier); + fsupplierEdit->setGeometry(QRect(160, 40, 511, 23)); + fsupplierEdit->setReadOnly(true); + famountEdit = new QDoubleSpinBox(dialog); + famountEdit->setObjectName(QString::fromUtf8("famountEdit")); + famountEdit->setGeometry(QRect(160, 100, 121, 24)); + famountEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + famountEdit->setAccelerated(true); + famountEdit->setDecimals(3); + famountEdit->setReadOnly(recipe->fermentables_use100); + famountEdit->setMaximum(100000.0); + famountEdit->setSingleStep(0.0010); + famountEdit->setValue(recipe->fermentables.at(recipe->fermentables_row).f_amount); + + pctEdit = new QDoubleSpinBox(dialog); + pctEdit->setObjectName(QString::fromUtf8("pctEdit")); + pctEdit->setGeometry(QRect(160, 130, 121, 24)); + pctEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + pctEdit->setAccelerated(true); + pctEdit->setDecimals(1); + if (recipe->fermentables_use100) { + if (recipe->fermentables.at(recipe->fermentables_row).f_adjust_to_total_100) + pctEdit->setReadOnly(true); + else + pctEdit->setReadOnly(false); + } else { + pctEdit->setReadOnly(true); + } + pctEdit->setMaximum(100.0); + pctEdit->setSingleStep(0.1); + pctEdit->setValue(recipe->fermentables.at(recipe->fermentables_row).f_percentage); + + faddedEdit = new QComboBox(dialog); + faddedEdit->setObjectName(QString::fromUtf8("faddedEdit")); + faddedEdit->setGeometry(QRect(160, 190, 161, 23)); + faddedEdit->addItem(tr("Mash")); + faddedEdit->addItem(tr("Boil")); + faddedEdit->addItem(tr("Fermentation")); + faddedEdit->addItem(tr("Lagering")); + faddedEdit->addItem(tr("Bottle")); + faddedEdit->addItem(tr("Kegs")); + faddedEdit->setCurrentIndex(recipe->fermentables.at(recipe->fermentables_row).f_added); + + to100Edit = new QCheckBox(dialog); + to100Edit->setObjectName(QString::fromUtf8("to100Edit")); + to100Edit->setGeometry(QRect(160, 160, 85, 21)); + to100Edit->setChecked(recipe->fermentables.at(recipe->fermentables_row).f_adjust_to_total_100); + + finstockEdit = new QCheckBox(dialog); + finstockEdit->setObjectName(QString::fromUtf8("instockEdit")); + finstockEdit->setGeometry(QRect(655, 70, 85, 21)); + finstockEdit->setChecked(true); + + fmaxEdit = new QDoubleSpinBox(dialog); + fmaxEdit->setObjectName(QString::fromUtf8("fmaxEdit")); + fmaxEdit->setGeometry(QRect(550, 130, 121, 24)); + fmaxEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + fmaxEdit->setReadOnly(true); + fmaxEdit->setButtonSymbols(QAbstractSpinBox::NoButtons); + fmaxEdit->setDecimals(1); + fmaxEdit->setValue(recipe->fermentables.at(recipe->fermentables_row).f_max_in_batch); + + ferment_instock_changed(true); + + connect(fselectEdit, QOverload::of(&QComboBox::currentIndexChanged), this, &EditRecipe::ferment_select_changed); + connect(famountEdit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &EditRecipe::ferment_amount_changed); + connect(pctEdit, QOverload::of(&QDoubleSpinBox::valueChanged), this, &EditRecipe::ferment_pct_changed); + connect(faddedEdit, QOverload::of(&QComboBox::currentIndexChanged), this, &EditRecipe::ferment_added_changed); + connect(to100Edit, &QCheckBox::stateChanged, this, &EditRecipe::ferment_to100_changed); + connect(finstockEdit, &QCheckBox::stateChanged, this, &EditRecipe::ferment_instock_changed); + connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject())); + connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept())); + + dialog->setModal(true); + dialog->exec(); + if (dialog->result() == QDialog::Rejected) { + qDebug() << "reject and rollback"; + recipe->fermentables[recipe->fermentables_row] = backup; + /* + * Recalculate the percentages + */ + double total = 0; + for (int i = 0; i < recipe->fermentables.size(); i++) + if (recipe->fermentables.at(i).f_added < 4) // Only before bottle/kegging + total += recipe->fermentables.at(i).f_amount; + recipe->fermentables_use100 = false; + for (int i = 0; i < recipe->fermentables.size(); i++) { + if (recipe->fermentables.at(i).f_adjust_to_total_100) + recipe->fermentables_use100 = true; + if (recipe->fermentables.at(i).f_added < 4) { + recipe->fermentables[i].f_percentage = recipe->fermentables.at(i).f_amount / total * 100; + } + } + } + + disconnect(fselectEdit, nullptr, nullptr, nullptr); + disconnect(famountEdit, nullptr, nullptr, nullptr); + disconnect(pctEdit, nullptr, nullptr, nullptr); + disconnect(faddedEdit, nullptr, nullptr, nullptr); + disconnect(to100Edit, nullptr, nullptr, nullptr); + disconnect(finstockEdit, nullptr, nullptr, nullptr); + disconnect(buttonBox, nullptr, nullptr, nullptr); + + emit refreshAll(); +} + +