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();
+}
+
+