src/EditRecipeTab2.cpp

changeset 127
475c8b8df67f
child 129
a9c19eaab018
equal deleted inserted replaced
126:3c013ef88a00 127:475c8b8df67f
1 /**
2 * EditRecipe.cpp is part of bmsapp.
3 *
4 * Tab 2, fermetables
5 *
6 * bmsapp is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * bmsapp is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20
21
22 bool EditRecipe::ferment_sort_test(const Fermentables &D1, const Fermentables &D2)
23 {
24 return (D1.f_added <= D2.f_added) && (D1.f_amount >= D2.f_amount) && (D1.f_color < D2.f_color);
25 }
26
27
28 void EditRecipe::to100Fermentables(int row)
29 {
30 if (recipe->fermentables.at(row).f_adjust_to_total_100) {
31 QWidget *pWidget = new QWidget();
32 QLabel *label = new QLabel;
33 label->setPixmap(QPixmap(":icons/silk/tick.png"));
34 QHBoxLayout *pLayout = new QHBoxLayout(pWidget);
35 pLayout->addWidget(label);
36 pLayout->setAlignment(Qt::AlignCenter);
37 pLayout->setContentsMargins(0, 0, 0, 0);
38 pWidget->setLayout(pLayout);
39 ui->fermentablesTable->setCellWidget(row, 9, pWidget);
40 } else {
41 ui->fermentablesTable->removeCellWidget(row, 9);
42 }
43 }
44
45
46 void EditRecipe::refreshFermentables()
47 {
48 QString w;
49 QWidget* pWidget;
50 QHBoxLayout* pLayout;
51 QTableWidgetItem *item;
52
53 qDebug() << "refreshFermentables" << recipe->fermentables.size();
54 std::sort(recipe->fermentables.begin(), recipe->fermentables.end(), ferment_sort_test);
55
56 /*
57 * During filling the table turn off the cellChanged signal because every cell that is filled
58 * triggers the cellChanged signal. The QTableWidget has no better signal to use.
59 */
60 this->ignoreChanges = true;
61
62 const QStringList labels({tr("Supplier"), tr("Fermentable"), tr("EBC"), tr("Type"), tr("Graintype"), tr("When"), tr("Yield"),
63 tr("Amount"), tr("Procent"), tr("100%"), tr("Delete"), tr("Edit") });
64 ui->fermentablesTable->setColumnCount(12);
65 ui->fermentablesTable->setColumnWidth(0, 150); /* Supplier */
66 ui->fermentablesTable->setColumnWidth(1, 225); /* Fermentable */
67 ui->fermentablesTable->setColumnWidth(2, 50); /* Color */
68 ui->fermentablesTable->setColumnWidth(3, 75); /* Type */
69 ui->fermentablesTable->setColumnWidth(4, 75); /* Graintype */
70 ui->fermentablesTable->setColumnWidth(5, 82); /* Added */
71 ui->fermentablesTable->setColumnWidth(6, 60); /* Yield */
72 ui->fermentablesTable->setColumnWidth(7, 90); /* Amount */
73 ui->fermentablesTable->setColumnWidth(8, 60); /* Procent */
74 ui->fermentablesTable->setColumnWidth(9, 50); /* 100% */
75 ui->fermentablesTable->setColumnWidth(10, 80); /* Delete */
76 ui->fermentablesTable->setColumnWidth(11, 80); /* Edit */
77 ui->fermentablesTable->setHorizontalHeaderLabels(labels);
78 ui->fermentablesTable->verticalHeader()->hide();
79 ui->fermentablesTable->setRowCount(recipe->fermentables.size());
80
81 for (int i = 0; i < recipe->fermentables.size(); i++) {
82
83 ui->fermentablesTable->setItem(i, 0, new QTableWidgetItem(recipe->fermentables.at(i).f_supplier));
84 ui->fermentablesTable->setItem(i, 1, new QTableWidgetItem(recipe->fermentables.at(i).f_name));
85
86 w = QString("%1").arg(recipe->fermentables.at(i).f_color, 1, 'f', 0, '0');
87 item = new QTableWidgetItem(w);
88 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
89 ui->fermentablesTable->setItem(i, 2, item);
90
91 item = new QTableWidgetItem(f_types[recipe->fermentables.at(i).f_type]);
92 item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
93 ui->fermentablesTable->setItem(i, 3, item);
94
95 item = new QTableWidgetItem(f_graintypes[recipe->fermentables.at(i).f_graintype]);
96 item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
97 ui->fermentablesTable->setItem(i, 4, item);
98
99 item = new QTableWidgetItem(f_added[recipe->fermentables.at(i).f_added]);
100 item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
101 ui->fermentablesTable->setItem(i, 5, item);
102
103 item = new QTableWidgetItem(QString("%1%").arg(recipe->fermentables.at(i).f_yield, 2, 'f', 1, '0'));
104 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
105 ui->fermentablesTable->setItem(i, 6, item);
106
107 item = new QTableWidgetItem(QString("%1 Kg").arg(recipe->fermentables.at(i).f_amount, 4, 'f', 3, '0'));
108 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
109 ui->fermentablesTable->setItem(i, 7, item);
110
111 if (recipe->fermentables.at(i).f_added < 4) {
112 item = new QTableWidgetItem(QString("%1%").arg(recipe->fermentables.at(i).f_percentage, 2, 'f', 1, '0'));
113 } else {
114 item = new QTableWidgetItem(QString("")); // Blank for bottling and kegging.
115 }
116 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
117 ui->fermentablesTable->setItem(i, 8, item);
118
119 to100Fermentables(i);
120
121 /* Add the Delete row button */
122 pWidget = new QWidget();
123 QPushButton* btn_dele = new QPushButton();
124 btn_dele->setObjectName(QString("%1").arg(i)); /* Send row with the button */
125 btn_dele->setText(tr("Delete"));
126 connect(btn_dele, SIGNAL(clicked()), this, SLOT(on_deleteFermentRow_clicked()));
127 pLayout = new QHBoxLayout(pWidget);
128 pLayout->addWidget(btn_dele);
129 pLayout->setContentsMargins(5, 0, 5, 0);
130 pWidget->setLayout(pLayout);
131 ui->fermentablesTable->setCellWidget(i, 10, pWidget);
132
133 pWidget = new QWidget();
134 QPushButton* btn_edit = new QPushButton();
135 btn_edit->setObjectName(QString("%1").arg(i)); /* Send row with the button */
136 btn_edit->setText(tr("Edit"));
137 connect(btn_edit, SIGNAL(clicked()), this, SLOT(on_editFermentRow_clicked()));
138 pLayout = new QHBoxLayout(pWidget);
139 pLayout->addWidget(btn_edit);
140 pLayout->setContentsMargins(5, 0, 5, 0);
141 pWidget->setLayout(pLayout);
142 ui->fermentablesTable->setCellWidget(i, 11, pWidget);
143 }
144 this->ignoreChanges = false;
145 }
146
147
148 void EditRecipe::calcFermentables()
149 {
150 int i;
151 double psugar = 0, pcara = 0, d, s = 0, x, color;
152 double vol = 0; // Volume sugars after boil
153 double addedS = 0; // Added sugars after boil
154 double addedmass = 0; // Added mass after boil
155 double mvol = 0; // Mash volume
156 double lintner = 0; // Total recipe lintner
157 double mashkg = 0;
158 double sugarsf = 0; // fermentable sugars mash + boil
159 double sugarsm = 0; // fermentable sugars in mash
160 double sugardensity = 1.611; // kg/l in solution
161 double mashtime = 0; // Total mash time
162 double mashtemp = 0; // Average mash temperature
163 double mashinfuse = 0; // Mash infuse amount
164 double colort = 0; // Colors srm * vol totals
165 double colorh = 0; // Colors ebc * vol * kt
166 double colorn = 0; // Colors ebc * pt * pct
167
168 qDebug() << "calcFermentables()";
169
170 /*
171 * Get average mashtemp and mashtime from the Mash schedule.
172 * It is possible that the schedule is not (yet) present.
173 */
174 if (recipe->mashs.size() > 0) {
175 for (i = 0; i < recipe->mashs.size(); i++) {
176 if (recipe->mashs.at(i).step_type == 0) // Infusion
177 mashinfuse += recipe->mashs.at(i).step_infuse_amount;
178 if (recipe->mashs.at(i).step_temp < 75) { // Ignore mashout
179 mashtime += recipe->mashs.at(i).step_time;
180 mashtemp += recipe->mashs.at(i).step_time * recipe->mashs.at(i).step_temp;
181 }
182 }
183 mashtemp = mashtemp / mashtime;
184 mvol = mashinfuse;
185 qDebug() << " mash time" << mashtime << "temp" << mashtemp << "infuse" << mashinfuse;
186 } else {
187 qDebug() << " no mash schedule";
188 }
189
190 if (recipe->fermentables.size() < 1) {
191 qDebug() << " no fermentables, return.";
192 recipe->est_og = 0.980;
193 ui->est_ogEdit->setValue(0.980);
194 ui->est_og2Edit->setValue(0.980);
195 ui->est_og3Edit->setValue(0.980);
196 ui->est_ogShow->setValue(0.980);
197 recipe->est_color = 0;
198 ui->est_colorEdit->setValue(0);
199 ui->est_colorEdit->setStyleSheet(Utils::ebc_to_style(0));
200 ui->est_color2Edit->setValue(0);
201 ui->est_color2Edit->setStyleSheet(Utils::ebc_to_style(0));
202 ui->est_colorShow->setValue(0);
203 ui->perc_mashShow->setValue(0);
204 ui->perc_sugarsShow->setValue(0);
205 ui->perc_caraShow->setValue(0);
206 ui->lintnerShow->setValue(0);
207 recipe->est_fg = 0.980;
208 ui->est_fgEdit->setValue(0.980);
209 ui->est_fg3Edit->setValue(0.980);
210 ui->est_fgShow->setValue(0.980);
211 ui->est_abvEdit->setValue(0);
212 ui->est_abv2Edit->setValue(0);
213 ui->est_abvShow->setValue(0);
214 recipe->est_abv = 0;
215 ui->calEdit->setValue(0);
216 return;
217 }
218 qDebug() << " adjust to 100" << recipe->fermentables_use100;
219
220 for (i = 0; i < recipe->fermentables.size(); i++) {
221 if (recipe->fermentables.at(i).f_type == 1 && recipe->fermentables.at(i).f_added < 4) // Sugars
222 psugar += recipe->fermentables.at(i).f_percentage;
223 if (recipe->fermentables.at(i).f_graintype == 2 && recipe->fermentables.at(i).f_added < 4) // Crystal/Cara
224 pcara += recipe->fermentables.at(i).f_percentage;
225 d = recipe->fermentables.at(i).f_amount * (recipe->fermentables.at(i).f_yield / 100) * (1 - recipe->fermentables.at(i).f_moisture / 100);
226 if (recipe->fermentables.at(i).f_added == 0) { // Mash
227 if (mvol > 0) { // If mash volume is known
228 mvol += recipe->fermentables.at(i).f_amount * recipe->fermentables.at(i).f_moisture / 100;
229 s += d;
230 }
231 d = ui->efficiencyEdit->value() / 100 * d;
232 sugarsm += d;
233 mashkg += recipe->fermentables.at(i).f_amount;
234 }
235 if (recipe->fermentables.at(i).f_added == 0 || recipe->fermentables.at(i).f_added == 1) // Mash or boil
236 sugarsf += d;
237 if (recipe->fermentables.at(i).f_added == 2 || recipe->fermentables.at(i).f_added == 3) { // Fermentation or lagering
238 x = (recipe->fermentables.at(i).f_yield / 100) * (1 - recipe->fermentables.at(i).f_moisture / 100);
239 addedS += recipe->fermentables.at(i).f_amount * x;
240 addedmass += recipe->fermentables.at(i).f_amount;
241 vol += (x * sugardensity + (1 - x) * 1) * recipe->fermentables.at(i).f_amount;
242 }
243 if (recipe->fermentables.at(i).f_added == 0 &&
244 (recipe->fermentables.at(i).f_type == 0 || recipe->fermentables.at(i).f_type == 4) &&
245 recipe->fermentables.at(i).f_color < 50) {
246 lintner += recipe->fermentables.at(i).f_diastatic_power * recipe->fermentables.at(i).f_amount;
247 }
248 if (recipe->fermentables.at(i).f_added < 4) {
249 colort += recipe->fermentables.at(i).f_amount * Utils::ebc_to_srm(recipe->fermentables.at(i).f_color);
250 colorh += recipe->fermentables.at(i).f_amount * recipe->fermentables.at(i).f_color * Utils::get_kt(recipe->fermentables.at(i).f_color);
251 colorn += (recipe->fermentables.at(i).f_percentage / 100) * recipe->fermentables.at(i).f_color; // For 8.6 Pt wort.
252 }
253 }
254 qDebug() << " colort" << colort << "colorh" << colorh << "colorn" << colorn;
255 qDebug() << " psugar" << psugar << "pcara" << pcara << "mvol" << mvol;
256 qDebug() << " sugarsf" << sugarsf << "sugarsm" << sugarsm;
257
258 double og = Utils::estimate_sg(sugarsf + addedS, ui->batch_sizeEdit->value());
259 qDebug() << " OG" << ui->est_ogEdit->value() << og;
260 recipe->est_og = og;
261 ui->est_ogEdit->setValue(og);
262 ui->est_og2Edit->setValue(og);
263 ui->est_og3Edit->setValue(og);
264 ui->est_ogShow->setValue(og);
265
266 recipe->preboil_sg = Utils::estimate_sg(sugarsm, ui->boil_sizeEdit->value());
267 qDebug() << " preboil SG" << recipe->preboil_sg;
268
269 /*
270 * Color of the wort
271 */
272 if (ui->color_methodEdit->currentIndex() == 4) { // Naudts
273 color = round(((Utils::sg_to_plato(og) / 8.6) * colorn) + (ui->boil_timeEdit->value() / 60));
274 } else if (ui->color_methodEdit->currentIndex() == 3) { // Hans Halberstadt
275 double bv = 0.925; // Beer loss efficiency
276 double sr = 0.95; // Mash and sparge efficiency
277 color = round((4.46 * bv * sr) / ui->batch_sizeEdit->value() * colorh);
278 } else {
279 double cw = colort / ui->batch_sizeEdit->value() * 8.34436;
280 color = Utils::kw_to_ebc(ui->color_methodEdit->currentIndex(), cw);
281 }
282 qDebug() << " color" << ui->est_colorEdit->value() << color << recipe->est_color;
283 recipe->est_color = color;
284 ui->est_colorEdit->setValue(color);
285 ui->est_colorEdit->setStyleSheet(Utils::ebc_to_style(color));
286 ui->est_color2Edit->setValue(color);
287 ui->est_color2Edit->setStyleSheet(Utils::ebc_to_style(color));
288 ui->est_colorShow->setValue(color);
289
290 /*
291 * We don't have a equipment profile in recipes,
292 * so we assume a certain guessed mashtun size.
293 */
294 ui->perc_mashShow->setValue(round(mashkg / (ui->boil_sizeEdit->value() / 3) * 100));
295 ui->perc_sugarsShow->setValue(round(psugar));
296 ui->perc_caraShow->setValue(round(pcara));
297 qDebug() << " lintner" << lintner << " mashkg" << mashkg << "final" << round(lintner / mashkg);
298 ui->lintnerShow->setValue(round(lintner / mashkg));
299
300 /*
301 * Calculate the apparant attenuation.
302 */
303 double svg = 0;
304 if (recipe->yeasts.size() > 0) {
305 for (i = 0; i < recipe->yeasts.size(); i++) {
306 if (recipe->yeasts.at(i).y_use == 0) { // Used in primary
307 if (recipe->yeasts.at(i).y_attenuation > svg)
308 svg = recipe->yeasts.at(i).y_attenuation; // Take the highest if multiple yeasts.
309 }
310 // TODO: brett or others in secondary.
311 }
312 qDebug() << " SVG" << svg;
313 }
314 if (svg == 0)
315 svg = 77.0;
316
317 double fg;
318 if (mashkg > 0 && mashinfuse > 0 && mashtime > 0 && mashtemp > 0)
319 fg = Utils::estimate_fg(psugar, pcara, mashinfuse / mashkg, mashtime, mashtemp, svg, og);
320 else
321 fg = Utils::estimate_fg(psugar, pcara, 0, 0, 0, svg, og);
322 qDebug() << " FG" << ui->est_fgEdit->value() << fg;
323 recipe->est_fg = fg;
324 ui->est_fgEdit->setValue(fg);
325 ui->est_fg3Edit->setValue(fg);
326 ui->est_fgShow->setValue(fg);
327
328 double abv = Utils::abvol(og, fg);
329 qDebug() << " ABV" << ui->est_abvEdit->value() << abv;
330 ui->est_abvEdit->setValue(abv);
331 ui->est_abv2Edit->setValue(abv);
332 ui->est_abvShow->setValue(abv);
333 recipe->est_abv = abv;
334
335 /*
336 * Calculate kilocalories/liter. Formula from brouwhulp.
337 * Take the alcohol and sugar parts and then combine.
338 */
339 double alc = 1881.22 * fg * (og - fg) / (1.775 - og);
340 double sug = 3550 * fg * (0.1808 * og + 0.8192 * fg - 1.0004);
341 ui->calEdit->setValue(round((alc + sug) / (12 * 0.0295735296)));
342 }
343
344
345 void EditRecipe::on_perc_mash_valueChanged(int value)
346 {
347 if (value < 90)
348 ui->perc_mashShow->setStyleSheet(bar_green);
349 else if (value < 100)
350 ui->perc_mashShow->setStyleSheet(bar_orange);
351 else
352 ui->perc_mashShow->setStyleSheet(bar_red);
353 }
354
355
356 void EditRecipe::on_perc_sugars_valueChanged(int value)
357 {
358 if (value < 20)
359 ui->perc_sugarsShow->setStyleSheet(bar_green);
360 else
361 ui->perc_sugarsShow->setStyleSheet(bar_red);
362 }
363
364
365 void EditRecipe::on_perc_cara_valueChanged(int value)
366 {
367 if (value < 25)
368 ui->perc_caraShow->setStyleSheet(bar_green);
369 else
370 ui->perc_caraShow->setStyleSheet(bar_red);
371 }
372
373
374 void EditRecipe::on_lintner_valueChanged(int value)
375 {
376 if (value < 30)
377 ui->lintnerShow->setStyleSheet(bar_red);
378 else if (value < 40)
379 ui->lintnerShow->setStyleSheet(bar_orange);
380 else
381 ui->lintnerShow->setStyleSheet(bar_green);
382 }
383
384
385 void EditRecipe::on_addFermentRow_clicked()
386 {
387 Fermentables newf;
388
389 qDebug() << "Add fermentable row";
390
391 for (int i = 0; i < recipe->fermentables.size(); i++) {
392 if (recipe->fermentables.at(i).f_amount == 0 && recipe->fermentables.at(i).f_color == 0)
393 return; // Add only one at a time.
394 }
395
396 newf.f_name = "Select one";
397 newf.f_origin = "";
398 newf.f_supplier = "";
399 newf.f_amount = 0;
400 newf.f_cost = 0;
401 newf.f_type = 0;
402 newf.f_yield = 0;
403 newf.f_color = 0;
404 newf.f_coarse_fine_diff = 0;
405 newf.f_moisture = 0;
406 newf.f_diastatic_power = 0;
407 newf.f_protein = 0;
408 newf.f_dissolved_protein = 0;
409 newf.f_max_in_batch = 100;
410 newf.f_graintype = 0;
411 newf.f_added = 0;
412 newf.f_recommend_mash = true;
413 newf.f_add_after_boil = false;
414 newf.f_adjust_to_total_100 = false;
415 newf.f_percentage = 0;
416 newf.f_di_ph = 0;
417 newf.f_acid_to_ph_57 = 0;
418
419 recipe->fermentables.append(newf);
420 emit refreshAll();
421 }
422
423
424 void EditRecipe::on_deleteFermentRow_clicked()
425 {
426 QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
427 int row = pb->objectName().toInt();
428 qDebug() << "Delete fermentable row" << row << recipe->fermentables.size();
429
430 if (recipe->fermentables.size() < 1)
431 return;
432
433 int rc = QMessageBox::warning(this, tr("Delete fermentable"), tr("Delete %1").arg(recipe->fermentables.at(row).f_name),
434 QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
435 if (rc == QMessageBox::No)
436 return;
437
438 this->ignoreChanges = true;
439 recipe->fermentables.removeAt(row);
440
441 /*
442 * Recalculate the percentages on the rows left.
443 */
444 double total = 0;
445 for (int i = 0; i < recipe->fermentables.size(); i++)
446 if (recipe->fermentables.at(i).f_added < 4) // Only before bottle/kegging
447 total += recipe->fermentables.at(i).f_amount;
448 for (int i = 0; i < recipe->fermentables.size(); i++)
449 if (recipe->fermentables.at(i).f_added < 4)
450 recipe->fermentables[i].f_percentage = recipe->fermentables.at(i).f_amount / total * 100;
451
452 this->ignoreChanges = false;
453 emit refreshAll();
454 }
455
456
457 void EditRecipe::ferment_amount_changed(double val)
458 {
459 QTableWidgetItem *item;
460 double total = 0, perc;
461
462 if (recipe->fermentables_use100)
463 return;
464
465 qDebug() << "ferment_amount_changed()" << recipe->fermentables_row << val;
466 this->ignoreChanges = true;
467
468 recipe->fermentables[recipe->fermentables_row].f_amount = val;
469 item = new QTableWidgetItem(QString("%1 Kg").arg(val, 4, 'f', 3, '0'));
470 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
471 ui->fermentablesTable->setItem(recipe->fermentables_row, 7, item);
472
473 for (int i = 0; i < recipe->fermentables.size(); i++)
474 if (recipe->fermentables.at(i).f_added < 4) // Only before bottle/kegging
475 total += recipe->fermentables.at(i).f_amount;
476 /*
477 * Recalculate the percentages
478 */
479 for (int i = 0; i < recipe->fermentables.size(); i++) {
480 if (recipe->fermentables.at(i).f_added < 4) {
481 perc = recipe->fermentables.at(i).f_amount / total * 100;
482 recipe->fermentables[i].f_percentage = perc;
483 item = new QTableWidgetItem(QString("%1%").arg(perc, 2, 'f', 1, '0'));
484 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
485 ui->fermentablesTable->setItem(i, 8, item);
486 if (i == recipe->fermentables_row)
487 this->pctEdit->setValue(perc);
488 }
489 }
490 this->ignoreChanges = false;
491 is_changed();
492 }
493
494
495 void EditRecipe::ferment_pct_changed(double val)
496 {
497 QTableWidgetItem *item;
498 double total = 0, row100 = -1;
499
500 if (! recipe->fermentables_use100)
501 return;
502
503 qDebug() << "ferment_pct_changed()" << recipe->fermentables_row << val;
504 /*
505 * Since we have arrived here, adjust_to_100 is active and
506 * this is not the entry to be adjusted to 100.
507 */
508 for (int i = 0; i < recipe->fermentables.size(); i++) {
509 if (recipe->fermentables.at(i).f_added < 4) // Only before bottle/kegging
510 total += recipe->fermentables.at(i).f_amount;
511 if (recipe->fermentables.at(i).f_adjust_to_total_100)
512 row100 = i;
513 }
514 double oldperc = recipe->fermentables.at(recipe->fermentables_row).f_percentage;
515 double diffp = val - oldperc;
516 double diffw = (diffp / 100) * total;
517 qDebug() << "row100" << row100 << "total" << total << "diff kg" << diffw << "diff %" << diffp;
518
519 this->ignoreChanges = true;
520 recipe->fermentables[recipe->fermentables_row].f_percentage += diffp;
521 recipe->fermentables[recipe->fermentables_row].f_amount += diffw;
522 recipe->fermentables[row100].f_percentage -= diffp;
523 recipe->fermentables[row100].f_amount -= diffw;
524
525 item = new QTableWidgetItem(QString("%1 Kg").arg(recipe->fermentables[recipe->fermentables_row].f_amount, 4, 'f', 3, '0'));
526 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
527 ui->fermentablesTable->setItem(recipe->fermentables_row, 7, item);
528 this->famountEdit->setValue(recipe->fermentables[recipe->fermentables_row].f_amount);
529
530 item = new QTableWidgetItem(QString("%1%").arg(recipe->fermentables[recipe->fermentables_row].f_percentage, 2, 'f', 1, '0'));
531 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
532 ui->fermentablesTable->setItem(recipe->fermentables_row, 8, item);
533
534 item = new QTableWidgetItem(QString("%1 Kg").arg(recipe->fermentables[row100].f_amount, 4, 'f', 3, '0'));
535 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
536 ui->fermentablesTable->setItem(row100, 7, item);
537
538 item = new QTableWidgetItem(QString("%1%").arg(recipe->fermentables[row100].f_percentage, 2, 'f', 1, '0'));
539 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
540 ui->fermentablesTable->setItem(row100, 8, item);
541
542 this->ignoreChanges = false;
543 is_changed();
544 }
545
546
547 void EditRecipe::ferment_to100_changed(bool val)
548 {
549 qDebug() << "ferment_to100_changed()" << recipe->fermentables_row << val << recipe->fermentables_use100;
550
551 /*
552 * Three scenario's.
553 * 1. There is no fermentable selected yet, just mark it.
554 * 2. There is current one is selected, deselect it.
555 * 3. There is another one selected, deselect and select this one.
556 */
557 if (! recipe->fermentables_use100 && val) {
558 /* Scenario 1. */
559 recipe->fermentables_use100 = true;
560 recipe->fermentables[recipe->fermentables_row].f_adjust_to_total_100 = true;
561 pctEdit->setReadOnly(false);
562 famountEdit->setReadOnly(true);
563 } else if (recipe->fermentables_use100 && recipe->fermentables[recipe->fermentables_row].f_adjust_to_total_100 && ! val) {
564 /* Scenario 2. */
565 recipe->fermentables[recipe->fermentables_row].f_adjust_to_total_100 = false;
566 recipe->fermentables_use100 = false;
567 pctEdit->setReadOnly(true);
568 famountEdit->setReadOnly(false);
569 } else if (recipe->fermentables_use100 && ! recipe->fermentables[recipe->fermentables_row].f_adjust_to_total_100 && val) {
570 /* Scenario 3. */
571 for (int i = 0; i < recipe->fermentables.size(); i++) {
572 recipe->fermentables[i].f_adjust_to_total_100 = false;
573 }
574 recipe->fermentables[recipe->fermentables_row].f_adjust_to_total_100 = true;
575 } else {
576 qDebug() << "bug";
577 return;
578 }
579
580 this->ignoreChanges = true;
581 for (int i = 0; i < recipe->fermentables.size(); i++) {
582 to100Fermentables(i);
583 }
584 this->ignoreChanges = false;
585 is_changed();
586 }
587
588 void EditRecipe::ferment_select_changed(int val)
589 {
590 QSqlQuery query;
591 bool instock = finstockEdit->isChecked();
592 QString w;
593 QTableWidgetItem *item;
594
595 if (val < 1)
596 return;
597
598 qDebug() << "ferment_select_changed()" << recipe->fermentables_row << val << instock;
599
600 /*
601 * Search the fermentable pointed by the index and instock flag.
602 */
603 QString sql = "SELECT name,origin,supplier,cost,type,yield,color,coarse_fine_diff,moisture,diastatic_power,protein,dissolved_protein,max_in_batch,"
604 "graintype,recommend_mash,add_after_boil,di_ph,acid_to_ph_57 FROM inventory_fermentables ";
605 if (instock)
606 sql.append("WHERE inventory > 0 ");
607 sql.append("ORDER BY supplier,name");
608 query.prepare(sql);
609 query.exec();
610 query.first();
611 for (int i = 0; i < (val - 1); i++) {
612 query.next();
613 }
614 qDebug() << "found" << query.value(2).toString() << query.value(0).toString();
615
616 /*
617 * Replace the fermentable record contents
618 */
619 this->ignoreChanges = true;
620 recipe->fermentables[recipe->fermentables_row].f_name = query.value(0).toString();
621 recipe->fermentables[recipe->fermentables_row].f_origin = query.value(1).toString();
622 recipe->fermentables[recipe->fermentables_row].f_supplier = query.value(2).toString();
623 recipe->fermentables[recipe->fermentables_row].f_cost = query.value(3).toDouble();
624 recipe->fermentables[recipe->fermentables_row].f_type = query.value(4).toInt();
625 recipe->fermentables[recipe->fermentables_row].f_yield = query.value(5).toDouble();
626 recipe->fermentables[recipe->fermentables_row].f_color = query.value(6).toDouble();
627 recipe->fermentables[recipe->fermentables_row].f_coarse_fine_diff = query.value(7).toDouble();
628 recipe->fermentables[recipe->fermentables_row].f_moisture = query.value(8).toDouble();
629 recipe->fermentables[recipe->fermentables_row].f_diastatic_power = query.value(9).toDouble();
630 recipe->fermentables[recipe->fermentables_row].f_protein = query.value(10).toDouble();
631 recipe->fermentables[recipe->fermentables_row].f_dissolved_protein = query.value(11).toDouble();
632 recipe->fermentables[recipe->fermentables_row].f_max_in_batch = query.value(12).toDouble();
633 recipe->fermentables[recipe->fermentables_row].f_graintype = query.value(13).toInt();
634 recipe->fermentables[recipe->fermentables_row].f_recommend_mash = query.value(14).toInt() ? true:false;
635 recipe->fermentables[recipe->fermentables_row].f_add_after_boil = query.value(15).toInt() ? true:false;
636 recipe->fermentables[recipe->fermentables_row].f_di_ph = query.value(16).toDouble();
637 recipe->fermentables[recipe->fermentables_row].f_acid_to_ph_57 = query.value(17).toDouble();
638
639 /*
640 * Update the visible fields
641 */
642 fnameEdit->setText(recipe->fermentables.at(recipe->fermentables_row).f_name);
643 fsupplierEdit->setText(recipe->fermentables.at(recipe->fermentables_row).f_supplier);
644 fmaxEdit->setValue(recipe->fermentables.at(recipe->fermentables_row).f_max_in_batch);
645
646 ui->fermentablesTable->setItem(recipe->fermentables_row, 0, new QTableWidgetItem(recipe->fermentables.at(recipe->fermentables_row).f_supplier));
647 ui->fermentablesTable->setItem(recipe->fermentables_row, 1, new QTableWidgetItem(recipe->fermentables.at(recipe->fermentables_row).f_name));
648
649 w = QString("%1").arg(recipe->fermentables.at(recipe->fermentables_row).f_color, 1, 'f', 0, '0');
650 item = new QTableWidgetItem(w);
651 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
652 ui->fermentablesTable->setItem(recipe->fermentables_row, 2, item);
653
654 item = new QTableWidgetItem(f_types[recipe->fermentables.at(recipe->fermentables_row).f_type]);
655 item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
656 ui->fermentablesTable->setItem(recipe->fermentables_row, 3, item);
657
658 item = new QTableWidgetItem(f_graintypes[recipe->fermentables.at(recipe->fermentables_row).f_graintype]);
659 item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
660 ui->fermentablesTable->setItem(recipe->fermentables_row, 4, item);
661
662 item = new QTableWidgetItem(QString("%1%").arg(recipe->fermentables.at(recipe->fermentables_row).f_yield, 2, 'f', 1, '0'));
663 item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
664 ui->fermentablesTable->setItem(recipe->fermentables_row, 6, item);
665
666 this->ignoreChanges = false;
667 calcFermentables();
668 is_changed();
669 }
670
671
672 void EditRecipe::ferment_instock_changed(bool val)
673 {
674 QSqlQuery query;
675
676 qDebug() << "ferment_instock_changed()" << recipe->fermentables_row << val;
677
678 this->fselectEdit->setCurrentIndex(-1);
679 this->fselectEdit->clear();
680 QString sql = "SELECT supplier,name,color,inventory FROM inventory_fermentables ";
681 if (val)
682 sql.append("WHERE inventory > 0 ");
683 sql.append("ORDER BY supplier,name");
684 query.prepare(sql);
685 query.exec();
686 query.first();
687 this->fselectEdit->addItem(""); // Start with empty value
688 for (int i = 0; i < query.size(); i++) {
689 this->fselectEdit->addItem(query.value(0).toString()+" - "+query.value(1).toString()+" ("+query.value(2).toString()+" EBC) "+
690 QString("%1 kg").arg(query.value(3).toDouble(), 4, 'f', 3, '0'));
691 query.next();
692 }
693 }
694
695
696 void EditRecipe::ferment_added_changed(int val)
697 {
698 qDebug() << "ferment_added_changed()" << recipe->fermentables_row << val;
699
700 this->ignoreChanges = true;
701 recipe->fermentables[recipe->fermentables_row].f_added = val;
702 QTableWidgetItem *item = new QTableWidgetItem(f_added[val]);
703 item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
704 ui->fermentablesTable->setItem(recipe->fermentables_row, 5, item);
705
706 double total = 0;
707 for (int i = 0; i < recipe->fermentables.size(); i++)
708 if (recipe->fermentables.at(i).f_added < 4) // Only before bottle/kegging
709 total += recipe->fermentables.at(i).f_amount;
710 for (int i = 0; i < recipe->fermentables.size(); i++)
711 if (recipe->fermentables.at(i).f_added < 4)
712 recipe->fermentables[i].f_percentage = recipe->fermentables.at(i).f_amount / total * 100;
713
714 this->ignoreChanges = false;
715 is_changed();
716 emit refreshAll();
717 }
718
719
720 void EditRecipe::on_editFermentRow_clicked()
721 {
722 QSqlQuery query;
723
724 QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
725 recipe->fermentables_row = pb->objectName().toInt();
726 qDebug() << "Edit fermentable row" << recipe->fermentables_row;
727 Fermentables backup = recipe->fermentables.at(recipe->fermentables_row);
728
729 QDialog* dialog = new QDialog(this);
730 dialog->resize(738, 287);
731 QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
732 buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
733 buttonBox->setGeometry(QRect(30, 240, 671, 32));
734 buttonBox->setLayoutDirection(Qt::LeftToRight);
735 buttonBox->setOrientation(Qt::Horizontal);
736 buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
737 buttonBox->setCenterButtons(true);
738 QLabel *nameLabel = new QLabel(dialog);
739 nameLabel->setObjectName(QString::fromUtf8("nameLabel"));
740 nameLabel->setText(tr("Current ingredient:"));
741 nameLabel->setGeometry(QRect(10, 10, 141, 20));
742 nameLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
743 QLabel *supplierLabel = new QLabel(dialog);
744 supplierLabel->setObjectName(QString::fromUtf8("supplierLabel"));
745 supplierLabel->setText(tr("Supplier:"));
746 supplierLabel->setGeometry(QRect(10, 40, 141, 20));
747 supplierLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
748 QLabel *amountLabel = new QLabel(dialog);
749 amountLabel->setObjectName(QString::fromUtf8("amountLabel"));
750 amountLabel->setText(tr("Amount in kg:"));
751 amountLabel->setGeometry(QRect(10, 100, 141, 20));
752 amountLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
753 QLabel *pctLabel = new QLabel(dialog);
754 pctLabel->setObjectName(QString::fromUtf8("pctLabel"));
755 pctLabel->setText(tr("Percentage in batch:"));
756 pctLabel->setGeometry(QRect(10, 130, 141, 20));
757 pctLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
758 QLabel *to100Label = new QLabel(dialog);
759 to100Label->setObjectName(QString::fromUtf8("to100Label"));
760 to100Label->setText(tr("Auto fill to 100%:"));
761 to100Label->setGeometry(QRect(10, 160, 141, 20));
762 to100Label->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
763 QLabel *addedLabel = new QLabel(dialog);
764 addedLabel->setObjectName(QString::fromUtf8("addedLabel"));
765 addedLabel->setText(tr("Use at:"));
766 addedLabel->setGeometry(QRect(10, 190, 141, 20));
767 addedLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
768 QLabel *selectLabel = new QLabel(dialog);
769 selectLabel->setObjectName(QString::fromUtf8("selectLabel"));
770 selectLabel->setText(tr("Select ingredient:"));
771 selectLabel->setGeometry(QRect(10, 70, 141, 20));
772 selectLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
773 QLabel *instockLabel = new QLabel(dialog);
774 instockLabel->setObjectName(QString::fromUtf8("instockLabel"));
775 instockLabel->setText(tr("In stock:"));
776 instockLabel->setGeometry(QRect(525, 70, 121, 20));
777 instockLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
778 QLabel *maxLabel = new QLabel(dialog);
779 maxLabel->setObjectName(QString::fromUtf8("maxLabel"));
780 maxLabel->setText(tr("Max in batch:"));
781 maxLabel->setGeometry(QRect(420, 130, 121, 20));
782 maxLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
783
784 fselectEdit = new QComboBox(dialog);
785 fselectEdit->setObjectName(QString::fromUtf8("fselectEdit"));
786 fselectEdit->setGeometry(QRect(160, 70, 371, 23));
787
788 fnameEdit = new QLineEdit(dialog);
789 fnameEdit->setObjectName(QString::fromUtf8("fnameEdit"));
790 fnameEdit->setText(recipe->fermentables.at(recipe->fermentables_row).f_name);
791 fnameEdit->setGeometry(QRect(160, 10, 511, 23));
792 fnameEdit->setReadOnly(true);
793 fsupplierEdit = new QLineEdit(dialog);
794 fsupplierEdit->setObjectName(QString::fromUtf8("fsupplierEdit"));
795 fsupplierEdit->setText(recipe->fermentables.at(recipe->fermentables_row).f_supplier);
796 fsupplierEdit->setGeometry(QRect(160, 40, 511, 23));
797 fsupplierEdit->setReadOnly(true);
798 famountEdit = new QDoubleSpinBox(dialog);
799 famountEdit->setObjectName(QString::fromUtf8("famountEdit"));
800 famountEdit->setGeometry(QRect(160, 100, 121, 24));
801 famountEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
802 famountEdit->setAccelerated(true);
803 famountEdit->setDecimals(3);
804 famountEdit->setReadOnly(recipe->fermentables_use100);
805 famountEdit->setMaximum(100000.0);
806 famountEdit->setSingleStep(0.0010);
807 famountEdit->setValue(recipe->fermentables.at(recipe->fermentables_row).f_amount);
808
809 pctEdit = new QDoubleSpinBox(dialog);
810 pctEdit->setObjectName(QString::fromUtf8("pctEdit"));
811 pctEdit->setGeometry(QRect(160, 130, 121, 24));
812 pctEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
813 pctEdit->setAccelerated(true);
814 pctEdit->setDecimals(1);
815 if (recipe->fermentables_use100) {
816 if (recipe->fermentables.at(recipe->fermentables_row).f_adjust_to_total_100)
817 pctEdit->setReadOnly(true);
818 else
819 pctEdit->setReadOnly(false);
820 } else {
821 pctEdit->setReadOnly(true);
822 }
823 pctEdit->setMaximum(100.0);
824 pctEdit->setSingleStep(0.1);
825 pctEdit->setValue(recipe->fermentables.at(recipe->fermentables_row).f_percentage);
826
827 faddedEdit = new QComboBox(dialog);
828 faddedEdit->setObjectName(QString::fromUtf8("faddedEdit"));
829 faddedEdit->setGeometry(QRect(160, 190, 161, 23));
830 faddedEdit->addItem(tr("Mash"));
831 faddedEdit->addItem(tr("Boil"));
832 faddedEdit->addItem(tr("Fermentation"));
833 faddedEdit->addItem(tr("Lagering"));
834 faddedEdit->addItem(tr("Bottle"));
835 faddedEdit->addItem(tr("Kegs"));
836 faddedEdit->setCurrentIndex(recipe->fermentables.at(recipe->fermentables_row).f_added);
837
838 to100Edit = new QCheckBox(dialog);
839 to100Edit->setObjectName(QString::fromUtf8("to100Edit"));
840 to100Edit->setGeometry(QRect(160, 160, 85, 21));
841 to100Edit->setChecked(recipe->fermentables.at(recipe->fermentables_row).f_adjust_to_total_100);
842
843 finstockEdit = new QCheckBox(dialog);
844 finstockEdit->setObjectName(QString::fromUtf8("instockEdit"));
845 finstockEdit->setGeometry(QRect(655, 70, 85, 21));
846 finstockEdit->setChecked(true);
847
848 fmaxEdit = new QDoubleSpinBox(dialog);
849 fmaxEdit->setObjectName(QString::fromUtf8("fmaxEdit"));
850 fmaxEdit->setGeometry(QRect(550, 130, 121, 24));
851 fmaxEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
852 fmaxEdit->setReadOnly(true);
853 fmaxEdit->setButtonSymbols(QAbstractSpinBox::NoButtons);
854 fmaxEdit->setDecimals(1);
855 fmaxEdit->setValue(recipe->fermentables.at(recipe->fermentables_row).f_max_in_batch);
856
857 ferment_instock_changed(true);
858
859 connect(fselectEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditRecipe::ferment_select_changed);
860 connect(famountEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditRecipe::ferment_amount_changed);
861 connect(pctEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditRecipe::ferment_pct_changed);
862 connect(faddedEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditRecipe::ferment_added_changed);
863 connect(to100Edit, &QCheckBox::stateChanged, this, &EditRecipe::ferment_to100_changed);
864 connect(finstockEdit, &QCheckBox::stateChanged, this, &EditRecipe::ferment_instock_changed);
865 connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));
866 connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));
867
868 dialog->setModal(true);
869 dialog->exec();
870 if (dialog->result() == QDialog::Rejected) {
871 qDebug() << "reject and rollback";
872 recipe->fermentables[recipe->fermentables_row] = backup;
873 /*
874 * Recalculate the percentages
875 */
876 double total = 0;
877 for (int i = 0; i < recipe->fermentables.size(); i++)
878 if (recipe->fermentables.at(i).f_added < 4) // Only before bottle/kegging
879 total += recipe->fermentables.at(i).f_amount;
880 recipe->fermentables_use100 = false;
881 for (int i = 0; i < recipe->fermentables.size(); i++) {
882 if (recipe->fermentables.at(i).f_adjust_to_total_100)
883 recipe->fermentables_use100 = true;
884 if (recipe->fermentables.at(i).f_added < 4) {
885 recipe->fermentables[i].f_percentage = recipe->fermentables.at(i).f_amount / total * 100;
886 }
887 }
888 }
889
890 disconnect(fselectEdit, nullptr, nullptr, nullptr);
891 disconnect(famountEdit, nullptr, nullptr, nullptr);
892 disconnect(pctEdit, nullptr, nullptr, nullptr);
893 disconnect(faddedEdit, nullptr, nullptr, nullptr);
894 disconnect(to100Edit, nullptr, nullptr, nullptr);
895 disconnect(finstockEdit, nullptr, nullptr, nullptr);
896 disconnect(buttonBox, nullptr, nullptr, nullptr);
897
898 emit refreshAll();
899 }
900
901

mercurial