Split recipes source in tabs.

Mon, 11 Apr 2022 17:22:43 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Mon, 11 Apr 2022 17:22:43 +0200
changeset 127
475c8b8df67f
parent 126
3c013ef88a00
child 128
0f4eee875ea6

Split recipes source in tabs.

src/EditRecipe.cpp file | annotate | diff | comparison | revisions
src/EditRecipeTab1.cpp file | annotate | diff | comparison | revisions
src/EditRecipeTab2.cpp file | annotate | diff | comparison | revisions
src/EditRecipeTab3.cpp file | annotate | diff | comparison | revisions
--- a/src/EditRecipe.cpp	Mon Apr 11 16:18:40 2022 +0200
+++ b/src/EditRecipe.cpp	Mon Apr 11 17:22:43 2022 +0200
@@ -519,325 +519,6 @@
 }
 
 
-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;
-}
-
-
-bool EditRecipe::hop_sort_test(const Hops &D1, const Hops &D2)
-{
-    if (D1.h_useat > D2.h_useat)
-	return false;
-    if (D1.h_useat < D2.h_useat)
-	return true;
-    /* Same useat moments, test time. */
-    if (D1.h_time < D2.h_time)
-	return false;
-    if (D1.h_time > D2.h_time)
-	return true;
-    /* Finally consider the amounts */
-    return (D1.h_amount > D2.h_amount);
-}
-
-
-void EditRecipe::refreshHops()
-{
-    QString w;
-    QWidget* pWidget;
-    QHBoxLayout* pLayout;
-    QTableWidgetItem *item;
-
-    qDebug() << "refreshHops" << recipe->hops.size();
-    std::sort(recipe->hops.begin(), recipe->hops.end(), hop_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("Origin"), tr("Hop"), tr("Type"), tr("Form"), tr("Alpha"), tr("Use at"), tr("Time"),
-                              tr("IBU"), tr("Amount"), tr("Delete"), tr("Edit") });
-
-    ui->hopsTable->setColumnCount(11);
-    ui->hopsTable->setColumnWidth(0, 150);     /* Origin	*/
-    ui->hopsTable->setColumnWidth(1, 225);     /* Hop		*/
-    ui->hopsTable->setColumnWidth(2,  75);     /* Type		*/
-    ui->hopsTable->setColumnWidth(3,  75);     /* Form          */
-    ui->hopsTable->setColumnWidth(4,  75);     /* Alpha%	*/
-    ui->hopsTable->setColumnWidth(5,  75);     /* Added         */
-    ui->hopsTable->setColumnWidth(6,  75);     /* Time		*/
-    ui->hopsTable->setColumnWidth(7,  60);     /* IBU		*/
-    ui->hopsTable->setColumnWidth(8,  75);     /* Amount	*/
-    ui->hopsTable->setColumnWidth(9,  80);     /* Delete        */
-    ui->hopsTable->setColumnWidth(10, 80);     /* Edit          */
-    ui->hopsTable->setHorizontalHeaderLabels(labels);
-    ui->hopsTable->verticalHeader()->hide();
-    ui->hopsTable->setRowCount(recipe->hops.size());
-
-    for (int i = 0; i < recipe->hops.size(); i++) {
-
-	ui->hopsTable->setItem(i, 0, new QTableWidgetItem(recipe->hops.at(i).h_origin));
-	ui->hopsTable->setItem(i, 1, new QTableWidgetItem(recipe->hops.at(i).h_name));
-
-	item = new QTableWidgetItem(h_types[recipe->hops.at(i).h_type]);
-        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
-        ui->hopsTable->setItem(i, 2, item);
-
-	item = new QTableWidgetItem(h_forms[recipe->hops.at(i).h_form]);
-        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
-        ui->hopsTable->setItem(i, 3, item);
-
-	item = new QTableWidgetItem(QString("%1%").arg(recipe->hops.at(i).h_alpha, 2, 'f', 1, '0'));
-        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
-        ui->hopsTable->setItem(i, 4, item);
-
-	item = new QTableWidgetItem(h_useat[recipe->hops.at(i).h_useat]);
-        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
-        ui->hopsTable->setItem(i, 5, item);
-
-	if (recipe->hops.at(i).h_useat == 2 || recipe->hops.at(i).h_useat == 4) {	// Boil or whirlpool
-	    item = new QTableWidgetItem(QString("%1 min.").arg(recipe->hops.at(i).h_time, 1, 'f', 0, '0'));
-	} else if (recipe->hops.at(i).h_useat == 5) {					// Dry-hop
-	    item = new QTableWidgetItem(QString("%1 days.").arg(recipe->hops.at(i).h_time / 1440, 1, 'f', 0, '0'));
-	} else {
-	    item = new QTableWidgetItem(QString(""));
-	}
-	item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
-        ui->hopsTable->setItem(i, 6, item);
-
-	double ibu = Utils::toIBU(recipe->hops.at(i).h_useat, recipe->hops.at(i).h_form, recipe->preboil_sg, recipe->batch_size, recipe->hops.at(i).h_amount,
-	                   recipe->hops.at(i).h_time, recipe->hops.at(i).h_alpha, recipe->ibu_method, 0, recipe->hops.at(i).h_time, 0);
-	item = new QTableWidgetItem(QString("%1").arg(ibu, 2, 'f', 1, '0'));
-        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
-        ui->hopsTable->setItem(i, 7, item);
-
-	if (recipe->hops.at(i).h_amount < 1.0) {
-	    item = new QTableWidgetItem(QString("%1 gr").arg(recipe->hops.at(i).h_amount * 1000.0, 2, 'f', 1, '0'));
-	} else {
-	    item = new QTableWidgetItem(QString("%1 kg").arg(recipe->hops.at(i).h_amount, 4, 'f', 3, '0'));
-	}
-        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
-        ui->hopsTable->setItem(i, 8, item);
-
-	/* 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_deleteHopRow_clicked()));
-        pLayout = new QHBoxLayout(pWidget);
-        pLayout->addWidget(btn_dele);
-        pLayout->setContentsMargins(5, 0, 5, 0);
-        pWidget->setLayout(pLayout);
-        ui->hopsTable->setCellWidget(i, 9, 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_editHopRow_clicked()));
-        pLayout = new QHBoxLayout(pWidget);
-        pLayout->addWidget(btn_edit);
-        pLayout->setContentsMargins(5, 0, 5, 0);
-        pWidget->setLayout(pLayout);
-        ui->hopsTable->setCellWidget(i, 10, pWidget);
-    }
-    this->ignoreChanges = false;
-}
-
-
-void EditRecipe::on_Flavour_valueChanged(int value)
-{
-    if (value < 20) {
-        ui->hop_tasteShow->setStyleSheet(bar_20);
-	ui->hop_tasteShow->setFormat(tr("Very low"));
-    } else if (value < 40) {
-	ui->hop_tasteShow->setStyleSheet(bar_40);
-	ui->hop_tasteShow->setFormat(tr("Low"));
-    } else if (value < 60) {
-        ui->hop_tasteShow->setStyleSheet(bar_60);
-        ui->hop_tasteShow->setFormat(tr("Moderate"));
-    } else if (value < 80) {
-        ui->hop_tasteShow->setStyleSheet(bar_80);
-        ui->hop_tasteShow->setFormat(tr("High"));
-    } else {
-        ui->hop_tasteShow->setStyleSheet(bar_100);
-	ui->hop_tasteShow->setFormat(tr("Very high"));
-    }
-}
-
-
-void EditRecipe::on_Aroma_valueChanged(int value)
-{
-    if (value < 20) {
-        ui->hop_aromaShow->setStyleSheet(bar_20);
-        ui->hop_aromaShow->setFormat(tr("Very low"));
-    } else if (value < 40) {
-        ui->hop_aromaShow->setStyleSheet(bar_40);
-        ui->hop_aromaShow->setFormat(tr("Low"));
-    } else if (value < 60) {
-        ui->hop_aromaShow->setStyleSheet(bar_60);
-        ui->hop_aromaShow->setFormat(tr("Moderate"));
-    } else if (value < 80) {
-        ui->hop_aromaShow->setStyleSheet(bar_80);
-        ui->hop_aromaShow->setFormat(tr("High"));
-    } else {
-        ui->hop_aromaShow->setStyleSheet(bar_100);
-        ui->hop_aromaShow->setFormat(tr("Very high"));
-    }
-}
-
-
-void EditRecipe::calcIBUs()
-{
-    double hop_flavour = 0, hop_aroma = 0, ibus = 0;
-
-    for (int i = 0; i < recipe->hops.size(); i++) {
-
-	ibus += Utils::toIBU(recipe->hops.at(i).h_useat, recipe->hops.at(i).h_form, recipe->preboil_sg, recipe->batch_size, recipe->hops.at(i).h_amount,
-                           recipe->hops.at(i).h_time, recipe->hops.at(i).h_alpha, recipe->ibu_method, 0, recipe->hops.at(i).h_time, 0);
-	hop_flavour += Utils::hopFlavourContribution(recipe->hops.at(i).h_time, recipe->batch_size, recipe->hops.at(i).h_useat, recipe->hops.at(i).h_amount);
-        hop_aroma += Utils::hopAromaContribution(recipe->hops.at(i).h_time, recipe->batch_size, recipe->hops.at(i).h_useat, recipe->hops.at(i).h_amount);
-    }
-
-    hop_flavour = round(hop_flavour * 1000.0 / 5.0) / 10;
-    hop_aroma = round(hop_aroma * 1000.0 / 6.0) / 10;
-    if (hop_flavour > 100)
-        hop_flavour = 100;
-    if (hop_aroma > 100)
-        hop_aroma = 100;
-    qDebug() << "ibu" << recipe->est_ibu << ibus << "flavour" << hop_flavour << "aroma" << hop_aroma;
-
-    recipe->est_ibu = ibus;
-    ui->est_ibuEdit->setValue(recipe->est_ibu);
-    ui->est_ibu2Edit->setValue(recipe->est_ibu);
-    ui->hop_tasteShow->setValue(hop_flavour);
-    ui->hop_aromaShow->setValue(hop_aroma);
-}
-
-
 void EditRecipe::refreshMiscs()
 {
 }
@@ -865,243 +546,6 @@
 }
 
 
-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);
-}
-
-
 /*
  * Window header, mark any change with '**'
  */
@@ -1454,6 +898,11 @@
 }
 
 
+#include "EditRecipeTab1.cpp"
+#include "EditRecipeTab2.cpp"
+#include "EditRecipeTab3.cpp"
+
+
 void EditRecipe::on_deleteButton_clicked()
 {
     QSqlQuery query;
@@ -1486,998 +935,6 @@
 }
 
 
-void EditRecipe::name_changed(QString name)
-{
-    recipe->name = name;
-    is_changed();
-}
-
-
-void EditRecipe::notes_changed()
-{
-    /* The text cannot be passed in a simple way :) */
-    recipe->notes = ui->notesEdit->toPlainText();
-    is_changed();
-}
-
-
-/*
- * New beerstyle is selected.
- */
-void EditRecipe::style_changed()
-{
-    QSqlQuery query;
-
-    if (ui->beerstyleEdit->currentIndex() < 1)
-	return;
-
-    query.prepare("SELECT * FROM profile_styles ORDER BY style_guide,style_letter,name");
-    query.exec();
-    query.first();
-    // Skip to the record index.
-    for (int i = 0; i < (ui->beerstyleEdit->currentIndex() - 1); i++) {
-        query.next();
-    }
-    // Set relevant fields and update ranges.
-    recipe->st_name = query.value(1).toString();
-    recipe->st_category = query.value(2).toString();
-    recipe->st_category_number = query.value(3).toInt();
-    recipe->st_letter = query.value(4).toString();
-    recipe->st_guide = query.value(5).toString();
-    recipe->st_type = query.value(6).toInt();
-    recipe->st_og_min = query.value(7).toDouble();
-    recipe->st_og_max = query.value(8).toDouble();
-    recipe->st_fg_min = query.value(9).toDouble();
-    recipe->st_fg_max = query.value(10).toDouble();
-    recipe->st_ibu_min = query.value(11).toDouble();
-    recipe->st_ibu_max = query.value(12).toDouble();
-    recipe->st_color_min = query.value(13).toDouble();
-    recipe->st_color_max = query.value(14).toDouble();
-    recipe->st_carb_min = query.value(15).toDouble();
-    recipe->st_carb_max = query.value(16).toDouble();
-    recipe->st_abv_min = query.value(17).toDouble();
-    recipe->st_abv_max = query.value(18).toDouble();
-
-    ui->st_nameEdit->setText(recipe->st_name);
-    ui->st_groupEdit->setText(recipe->st_letter);
-    ui->st_guideEdit->setText(recipe->st_guide);
-    ui->st_catEdit->setText(recipe->st_category);
-    ui->st_catnrEdit->setText(QString("%1").arg(recipe->st_category_number));
-    ui->st_typeEdit->setText(s_types[recipe->st_type]);
-
-    ui->est_ogShow->setRange(query.value(7).toDouble(), query.value(8).toDouble());
-    ui->est_fgShow->setRange(query.value(9).toDouble(), query.value(10).toDouble());
-    ui->est_ibuShow->setRange(query.value(11).toDouble(), query.value(12).toDouble());
-    ui->est_colorShow->setRange(query.value(13).toDouble(), query.value(14).toDouble());
-    ui->est_carbShow->setRange(query.value(15).toDouble(), query.value(16).toDouble());
-    ui->est_abvShow->setRange(query.value(17).toDouble(), query.value(18).toDouble());
-
-    is_changed();
-}
-
-
-void EditRecipe::colormethod_changed()
-{
-    calcFermentables();
-    is_changed();
-}
-
-
-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_addHopRow_clicked()
-{
-    Hops newh;
-
-    qDebug() << "Add hop row";
-
-    for (int i = 0; i < recipe->hops.size(); i++) {
-        if (recipe->hops.at(i).h_amount == 0 && recipe->hops.at(i).h_alpha == 0)
-            return;     // Add only one at a time.
-    }
-
-    emit refreshAll();
-}
-
-
-void EditRecipe::on_deleteFermentRow_clicked()
-{
-    QPushButton *pb = qobject_cast<QPushButton *>(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::on_deleteHopRow_clicked()
-{
-    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
-    int row = pb->objectName().toInt();
-    qDebug() << "Delete hop row" << row << recipe->hops.size();
-
-    if (recipe->hops.size() < 1)
-        return;
-
-    int rc = QMessageBox::warning(this, tr("Delete hop"), tr("Delete %1").arg(recipe->hops.at(row).h_name),
-                    QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
-    if (rc == QMessageBox::No)
-        return;
-
-
-}
-
-
-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::hop_amount_changed(double val)
-{
-    QTableWidgetItem *item;
-
-    qDebug() << "hop_amount_changed()" << recipe->hops_row << val;
-    this->ignoreChanges = true;
-
-    recipe->hops[recipe->hops_row].h_amount = val / 1000.0;
-    item = new QTableWidgetItem(QString("%1 gr").arg(val, 2, 'f', 1, '0'));
-    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
-    ui->hopsTable->setItem(recipe->hops_row, 8, item);
-
-    double ibu = Utils::toIBU(recipe->hops.at(recipe->hops_row).h_useat, recipe->hops.at(recipe->hops_row).h_form, recipe->preboil_sg,
-                              recipe->batch_size, recipe->hops.at(recipe->hops_row).h_amount, recipe->hops.at(recipe->hops_row).h_time,
-                              recipe->hops.at(recipe->hops_row).h_alpha, recipe->ibu_method, 0, recipe->hops.at(recipe->hops_row).h_time, 0);
-
-    ibuEdit->setValue(ibu);
-    item = new QTableWidgetItem(QString("%1").arg(ibu, 2, 'f', 1, '0'));
-    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
-    ui->hopsTable->setItem(recipe->hops_row, 7, item);
-
-    this->ignoreChanges = false;
-    calcIBUs();
-    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::hop_time_changed(int val)
-{
-    QTableWidgetItem *item;
-
-    qDebug() << "hop_time_changed()" << recipe->hops_row << val;
-
-    this->ignoreChanges = true;
-    recipe->hops[recipe->hops_row].h_time = val;
-
-    if (recipe->hops.at(recipe->hops_row).h_useat == 2 || recipe->hops.at(recipe->hops_row).h_useat == 4) {       // Boil or whirlpool
-        item = new QTableWidgetItem(QString("%1 min.").arg(val, 1, 'f', 0, '0'));
-    } else if (recipe->hops.at(recipe->hops_row).h_useat == 5) {                                   // Dry-hop
-        item = new QTableWidgetItem(QString("%1 days.").arg(val / 1440, 1, 'f', 0, '0'));
-    } else {
-        item = new QTableWidgetItem(QString(""));
-    }
-    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
-    ui->hopsTable->setItem(recipe->hops_row, 6, item);
-
-    double ibu = Utils::toIBU(recipe->hops.at(recipe->hops_row).h_useat, recipe->hops.at(recipe->hops_row).h_form, recipe->preboil_sg,
-                              recipe->batch_size, recipe->hops.at(recipe->hops_row).h_amount, recipe->hops.at(recipe->hops_row).h_time,
- 			      recipe->hops.at(recipe->hops_row).h_alpha, recipe->ibu_method, 0, recipe->hops.at(recipe->hops_row).h_time, 0);
-
-    ibuEdit->setValue(ibu);
-    item = new QTableWidgetItem(QString("%1").arg(ibu, 2, 'f', 1, '0'));
-    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
-    ui->hopsTable->setItem(recipe->hops_row, 7, item);
-
-    this->ignoreChanges = false;
-    calcIBUs();
-    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::hop_select_changed(int val)
-{
-    QSqlQuery query;
-    bool instock = hinstockEdit->isChecked();
-    QString w;
-    QTableWidgetItem *item;
-
-    if (val < 1)
-        return;
-
-    qDebug() << "hop_select_changed()" << recipe->fermentables_row << val << instock;
-
-    /*
-     * Search the hop pointed by the index and instock flag.
-     */
-    QString sql = "SELECT name,origin,alpha,beta,humulene,caryophyllene,cohumulone,myrcene,hsi,total_oil,type,form,cost FROM inventory_hops ";
-    if (instock)
-        sql.append("WHERE inventory > 0 ");
-    sql.append("ORDER BY origin,name");
-    query.prepare(sql);
-    query.exec();
-    query.first();
-    for (int i = 0; i < (val - 1); i++) {
-        query.next();
-    }
-    qDebug() << "found" << query.value(1).toString() << query.value(0).toString();
-
-    /*
-     * Replace the hop record contents
-     */
-    this->ignoreChanges = true;
-    recipe->hops[recipe->hops_row].h_name = query.value(0).toString();
-    recipe->hops[recipe->hops_row].h_origin = query.value(1).toString();
-    recipe->hops[recipe->hops_row].h_alpha = query.value(2).toDouble();
-    recipe->hops[recipe->hops_row].h_beta = query.value(3).toDouble();
-    recipe->hops[recipe->hops_row].h_humulene = query.value(4).toDouble();
-    recipe->hops[recipe->hops_row].h_caryophyllene = query.value(5).toDouble();
-    recipe->hops[recipe->hops_row].h_cohumulone = query.value(6).toDouble();
-    recipe->hops[recipe->hops_row].h_myrcene = query.value(7).toDouble();
-    recipe->hops[recipe->hops_row].h_hsi = query.value(8).toDouble();
-    recipe->hops[recipe->hops_row].h_total_oil = query.value(9).toDouble();
-    recipe->hops[recipe->hops_row].h_type = query.value(10).toInt();
-    recipe->hops[recipe->hops_row].h_form = query.value(11).toInt();
-    recipe->hops[recipe->hops_row].h_cost = query.value(12).toDouble();
-
-    /*
-     * Update the visible fields
-     */
-    hnameEdit->setText(recipe->hops.at(recipe->hops_row).h_name);
-    horiginEdit->setText(recipe->hops.at(recipe->hops_row).h_origin);
-
-    double ibu = Utils::toIBU(recipe->hops.at(recipe->hops_row).h_useat, recipe->hops.at(recipe->hops_row).h_form, recipe->preboil_sg,
-		              recipe->batch_size, recipe->hops.at(recipe->hops_row).h_amount, recipe->hops.at(recipe->hops_row).h_time,
-			      recipe->hops.at(recipe->hops_row).h_alpha, recipe->ibu_method, 0, recipe->hops.at(recipe->hops_row).h_time, 0);
-    ibuEdit->setValue(ibu);
-
-    ui->hopsTable->setItem(recipe->hops_row, 0, new QTableWidgetItem(recipe->hops.at(recipe->hops_row).h_origin));
-    ui->hopsTable->setItem(recipe->hops_row, 1, new QTableWidgetItem(recipe->hops.at(recipe->hops_row).h_name));
-
-    item = new QTableWidgetItem(h_types[recipe->hops.at(recipe->hops_row).h_type]);
-    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
-    ui->hopsTable->setItem(recipe->hops_row, 2, item);
-
-    item = new QTableWidgetItem(h_forms[recipe->hops.at(recipe->hops_row).h_form]);
-    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
-    ui->hopsTable->setItem(recipe->hops_row, 3, item);
-
-    item = new QTableWidgetItem(QString("%1%").arg(recipe->hops.at(recipe->hops_row).h_alpha, 2, 'f', 1, '0'));
-    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
-    ui->hopsTable->setItem(recipe->hops_row, 4, item);
-
-    item = new QTableWidgetItem(QString("%1").arg(ibu, 2, 'f', 1, '0'));
-    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
-    ui->hopsTable->setItem(recipe->hops_row, 7, item);
-
-    this->ignoreChanges = false;
-    calcIBUs();
-    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::hop_instock_changed(bool val)
-{
-    QSqlQuery query;
-
-    qDebug() << "hop_instock_changed()" << recipe->hops_row << val;
-
-    this->hselectEdit->setCurrentIndex(-1);
-    this->hselectEdit->clear();
-    QString sql = "SELECT origin,name,alpha,inventory FROM inventory_hops ";
-    if (val)
-        sql.append("WHERE inventory > 0 ");
-    sql.append("ORDER BY origin,name");
-    query.prepare(sql);
-    query.exec();
-    query.first();
-    this->hselectEdit->addItem("");      // Start with empty value
-    for (int i = 0; i < query.size(); i++) {
-        this->hselectEdit->addItem(query.value(0).toString()+" - "+query.value(1).toString()+" ("+query.value(2).toString()+"%) "+
-                        QString("%1 gr").arg(query.value(3).toDouble() * 1000.0, 2, 'f', 1, '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::hop_useat_changed(int val)
-{
-    qDebug() << "hop_useat_changed()" << recipe->hops_row << val;
-
-    this->ignoreChanges = true;
-    recipe->hops[recipe->hops_row].h_useat = val;
-    QTableWidgetItem *item = new QTableWidgetItem(h_useat[val]);
-    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
-    ui->hopsTable->setItem(recipe->hops_row, 5, item);
-
-    if (val == 2 || val == 4) {	// Boil or whirlpool
-	htimeLabel->setText(tr("Time in minutes:"));
-        htimeEdit->setValue(recipe->hops.at(recipe->hops_row).h_time);
-	htimeEdit->setReadOnly(false);
-    } else if (val == 5) {	// Dry-hop
-        htimeLabel->setText(tr("Time in days:"));
-	htimeEdit->setValue(recipe->hops.at(recipe->hops_row).h_time / 1440);
-	htimeEdit->setReadOnly(false);
-    } else {
-        htimeLabel->setText("");
-	htimeEdit->setValue(0);
-	htimeEdit->setReadOnly(true);
-    }
-
-    this->ignoreChanges = false;
-    is_changed();
-    emit refreshAll();
-}
-
-
-void EditRecipe::on_editFermentRow_clicked()
-{
-    QSqlQuery query;
-
-    QPushButton *pb = qobject_cast<QPushButton *>(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<int>::of(&QComboBox::currentIndexChanged), this, &EditRecipe::ferment_select_changed);
-    connect(famountEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditRecipe::ferment_amount_changed);
-    connect(pctEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditRecipe::ferment_pct_changed);
-    connect(faddedEdit, QOverload<int>::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();
-}
-
-
-void EditRecipe::on_editHopRow_clicked()
-{
-    QSqlQuery query;
-
-    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
-    recipe->hops_row = pb->objectName().toInt();
-    qDebug() << "Edit hop row" << recipe->hops_row;
-    Hops backup = recipe->hops.at(recipe->hops_row);
-
-    QDialog* dialog = new QDialog(this);
-    dialog->resize(738, 260);
-    QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
-    buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
-    buttonBox->setGeometry(QRect(30, 210, 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 hop:"));
-    nameLabel->setGeometry(QRect(10, 10, 141, 20));
-    nameLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
-    QLabel *originLabel = new QLabel(dialog);
-    originLabel->setObjectName(QString::fromUtf8("originLabel"));
-    originLabel->setText(tr("Origin:"));
-    originLabel->setGeometry(QRect(10, 40, 141, 20));
-    originLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
-    QLabel *amountLabel = new QLabel(dialog);
-    amountLabel->setObjectName(QString::fromUtf8("amountLabel"));
-    amountLabel->setText(tr("Amount in gr:"));
-    amountLabel->setGeometry(QRect(10, 100, 141, 20));
-    amountLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
-    htimeLabel = new QLabel(dialog);
-    htimeLabel->setObjectName(QString::fromUtf8("htimeLabel"));
-    if (recipe->hops.at(recipe->hops_row).h_useat == 5)		// Dry-hop
-	htimeLabel->setText(tr("Time in days:"));
-    else if (recipe->hops.at(recipe->hops_row).h_useat == 2 || recipe->hops.at(recipe->hops_row).h_useat == 4)	// Boil or whirlpool
-    	htimeLabel->setText(tr("Time in minutes:"));
-    else
-	htimeLabel->setText("");
-
-    htimeLabel->setGeometry(QRect(10, 130, 141, 20));
-    htimeLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
-    QLabel *useatLabel = new QLabel(dialog);
-    useatLabel->setObjectName(QString::fromUtf8("useatLabel"));
-    useatLabel->setText(tr("Use at:"));
-    useatLabel->setGeometry(QRect(10, 160, 141, 20));
-    useatLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
-    QLabel *selectLabel = new QLabel(dialog);
-    selectLabel->setObjectName(QString::fromUtf8("selectLabel"));
-    selectLabel->setText(tr("Select hop:"));
-    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 *ibuLabel = new QLabel(dialog);
-    ibuLabel->setObjectName(QString::fromUtf8("maxLabel"));
-    ibuLabel->setText(tr("Bitterness IBU:"));
-    ibuLabel->setGeometry(QRect(420, 130, 121, 20));
-    ibuLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
-
-    hselectEdit = new QComboBox(dialog);
-    hselectEdit->setObjectName(QString::fromUtf8("selectEdit"));
-    hselectEdit->setGeometry(QRect(160, 70, 371, 23));
-
-    hnameEdit = new QLineEdit(dialog);
-    hnameEdit->setObjectName(QString::fromUtf8("hnameEdit"));
-    hnameEdit->setText(recipe->hops.at(recipe->hops_row).h_name);
-    hnameEdit->setGeometry(QRect(160, 10, 511, 23));
-    hnameEdit->setReadOnly(true);
-    horiginEdit = new QLineEdit(dialog);
-    horiginEdit->setObjectName(QString::fromUtf8("horiginEdit"));
-    horiginEdit->setText(recipe->hops.at(recipe->hops_row).h_origin);
-    horiginEdit->setGeometry(QRect(160, 40, 511, 23));
-    horiginEdit->setReadOnly(true);
-    hamountEdit = new QDoubleSpinBox(dialog);
-    hamountEdit->setObjectName(QString::fromUtf8("hamountEdit"));
-    hamountEdit->setGeometry(QRect(160, 100, 121, 24));
-    hamountEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
-    hamountEdit->setAccelerated(true);
-    hamountEdit->setDecimals(1);
-    hamountEdit->setMaximum(1000000.0);
-    hamountEdit->setSingleStep(0.5);
-    hamountEdit->setValue(recipe->hops.at(recipe->hops_row).h_amount * 1000.0);
-    htimeEdit = new QSpinBox(dialog);
-    htimeEdit->setObjectName(QString::fromUtf8("htimeEdit"));
-    htimeEdit->setGeometry(QRect(160, 130, 121, 24));
-    htimeEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
-    htimeEdit->setAccelerated(true);
-    htimeEdit->setMaximum(10000.0);
-    if (recipe->hops.at(recipe->hops_row).h_useat == 2 || recipe->hops.at(recipe->hops_row).h_useat == 4) {	// Boil or whirlpool
-	htimeEdit->setValue(recipe->hops.at(recipe->hops_row).h_time);
-	htimeEdit->setReadOnly(false);
-    } else if (recipe->hops.at(recipe->hops_row).h_useat == 5){	// Dry-hop
-	htimeEdit->setValue(recipe->hops.at(recipe->hops_row).h_time / 1440);
-	htimeEdit->setReadOnly(false);
-    } else {
-	htimeEdit->setReadOnly(true);
-    }
-    useatEdit = new QComboBox(dialog);
-    useatEdit->setObjectName(QString::fromUtf8("useatEdit"));
-    useatEdit->setGeometry(QRect(160, 160, 161, 23));
-    useatEdit->addItem(tr("Mash"));
-    useatEdit->addItem(tr("First wort"));
-    useatEdit->addItem(tr("Boil"));
-    useatEdit->addItem(tr("Aroma"));
-    useatEdit->addItem(tr("Whirlpool"));
-    useatEdit->addItem(tr("Dry hop"));
-    useatEdit->setCurrentIndex(recipe->hops.at(recipe->hops_row).h_useat);
-
-    hinstockEdit = new QCheckBox(dialog);
-    hinstockEdit->setObjectName(QString::fromUtf8("hinstockEdit"));
-    hinstockEdit->setGeometry(QRect(655, 70, 85, 21));
-    hinstockEdit->setChecked(true);
-
-    ibuEdit = new QDoubleSpinBox(dialog);
-    ibuEdit->setObjectName(QString::fromUtf8("ibuEdit"));
-    ibuEdit->setGeometry(QRect(550, 130, 121, 24));
-    ibuEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
-    ibuEdit->setReadOnly(true);
-    ibuEdit->setButtonSymbols(QAbstractSpinBox::NoButtons);
-    ibuEdit->setDecimals(1);
-    double ibu = Utils::toIBU(recipe->hops.at(recipe->hops_row).h_useat, recipe->hops.at(recipe->hops_row).h_form, recipe->preboil_sg,
-                              recipe->batch_size, recipe->hops.at(recipe->hops_row).h_amount, recipe->hops.at(recipe->hops_row).h_time,
-                              recipe->hops.at(recipe->hops_row).h_alpha, recipe->ibu_method, 0, recipe->hops.at(recipe->hops_row).h_time, 0);
-    ibuEdit->setValue(ibu);
-
-    hop_instock_changed(true);
-
-    connect(hselectEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditRecipe::hop_select_changed);
-    connect(hamountEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditRecipe::hop_amount_changed);
-    connect(htimeEdit, QOverload<int>::of(&QSpinBox::valueChanged), this, &EditRecipe::hop_time_changed);
-    connect(useatEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditRecipe::hop_useat_changed);
-    connect(hinstockEdit, &QCheckBox::stateChanged, this, &EditRecipe::hop_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->hops[recipe->hops_row] = backup;
-    } else {
-	/* Clear time if hop is not used for boil, whirlpool or dry-hop. */
-	if (! (recipe->hops.at(recipe->hops_row).h_useat == 2 ||
-	       recipe->hops.at(recipe->hops_row).h_useat == 4 ||
-	       recipe->hops.at(recipe->hops_row).h_useat == 5)) {
-	    if (recipe->hops.at(recipe->hops_row).h_time) {
-		recipe->hops[recipe->hops_row].h_time = 0;
-		is_changed();
-	    }
-	}
-    }
-
-    disconnect(hselectEdit, nullptr, nullptr, nullptr);
-    disconnect(hamountEdit, nullptr, nullptr, nullptr);
-    disconnect(htimeEdit, nullptr, nullptr, nullptr);
-    disconnect(useatEdit, nullptr, nullptr, nullptr);
-    disconnect(hinstockEdit, nullptr, nullptr, nullptr);
-    disconnect(buttonBox, nullptr, nullptr, nullptr);
-
-    emit refreshAll();
-}
-
-
 void EditRecipe::on_quitButton_clicked()
 {
     if (this->textIsChanged) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EditRecipeTab1.cpp	Mon Apr 11 17:22:43 2022 +0200
@@ -0,0 +1,98 @@
+/**
+ * EditRecipe.cpp is part of bmsapp.
+ *
+ * Tab 1, generic settings.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+
+void EditRecipe::name_changed(QString name)
+{
+    recipe->name = name;
+    is_changed();
+}
+
+
+void EditRecipe::notes_changed()
+{
+    /* The text cannot be passed in a simple way :) */
+    recipe->notes = ui->notesEdit->toPlainText();
+    is_changed();
+}
+
+
+/*
+ * New beerstyle is selected.
+ */
+void EditRecipe::style_changed()
+{
+    QSqlQuery query;
+
+    if (ui->beerstyleEdit->currentIndex() < 1)
+	return;
+
+    query.prepare("SELECT * FROM profile_styles ORDER BY style_guide,style_letter,name");
+    query.exec();
+    query.first();
+    // Skip to the record index.
+    for (int i = 0; i < (ui->beerstyleEdit->currentIndex() - 1); i++) {
+        query.next();
+    }
+    // Set relevant fields and update ranges.
+    recipe->st_name = query.value(1).toString();
+    recipe->st_category = query.value(2).toString();
+    recipe->st_category_number = query.value(3).toInt();
+    recipe->st_letter = query.value(4).toString();
+    recipe->st_guide = query.value(5).toString();
+    recipe->st_type = query.value(6).toInt();
+    recipe->st_og_min = query.value(7).toDouble();
+    recipe->st_og_max = query.value(8).toDouble();
+    recipe->st_fg_min = query.value(9).toDouble();
+    recipe->st_fg_max = query.value(10).toDouble();
+    recipe->st_ibu_min = query.value(11).toDouble();
+    recipe->st_ibu_max = query.value(12).toDouble();
+    recipe->st_color_min = query.value(13).toDouble();
+    recipe->st_color_max = query.value(14).toDouble();
+    recipe->st_carb_min = query.value(15).toDouble();
+    recipe->st_carb_max = query.value(16).toDouble();
+    recipe->st_abv_min = query.value(17).toDouble();
+    recipe->st_abv_max = query.value(18).toDouble();
+
+    ui->st_nameEdit->setText(recipe->st_name);
+    ui->st_groupEdit->setText(recipe->st_letter);
+    ui->st_guideEdit->setText(recipe->st_guide);
+    ui->st_catEdit->setText(recipe->st_category);
+    ui->st_catnrEdit->setText(QString("%1").arg(recipe->st_category_number));
+    ui->st_typeEdit->setText(s_types[recipe->st_type]);
+
+    ui->est_ogShow->setRange(query.value(7).toDouble(), query.value(8).toDouble());
+    ui->est_fgShow->setRange(query.value(9).toDouble(), query.value(10).toDouble());
+    ui->est_ibuShow->setRange(query.value(11).toDouble(), query.value(12).toDouble());
+    ui->est_colorShow->setRange(query.value(13).toDouble(), query.value(14).toDouble());
+    ui->est_carbShow->setRange(query.value(15).toDouble(), query.value(16).toDouble());
+    ui->est_abvShow->setRange(query.value(17).toDouble(), query.value(18).toDouble());
+
+    is_changed();
+}
+
+
+void EditRecipe::colormethod_changed()
+{
+    calcFermentables();
+    is_changed();
+}
+
+
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+
+
+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<QPushButton *>(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<QPushButton *>(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<int>::of(&QComboBox::currentIndexChanged), this, &EditRecipe::ferment_select_changed);
+    connect(famountEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditRecipe::ferment_amount_changed);
+    connect(pctEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditRecipe::ferment_pct_changed);
+    connect(faddedEdit, QOverload<int>::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();
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EditRecipeTab3.cpp	Mon Apr 11 17:22:43 2022 +0200
@@ -0,0 +1,612 @@
+/**
+ * EditRecipe.cpp is part of bmsapp.
+ *
+ * Tab 3, hops
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+
+
+bool EditRecipe::hop_sort_test(const Hops &D1, const Hops &D2)
+{
+    if (D1.h_useat > D2.h_useat)
+	return false;
+    if (D1.h_useat < D2.h_useat)
+	return true;
+    /* Same useat moments, test time. */
+    if (D1.h_time < D2.h_time)
+	return false;
+    if (D1.h_time > D2.h_time)
+	return true;
+    /* Finally consider the amounts */
+    return (D1.h_amount > D2.h_amount);
+}
+
+
+void EditRecipe::refreshHops()
+{
+    QString w;
+    QWidget* pWidget;
+    QHBoxLayout* pLayout;
+    QTableWidgetItem *item;
+
+    qDebug() << "refreshHops" << recipe->hops.size();
+    std::sort(recipe->hops.begin(), recipe->hops.end(), hop_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("Origin"), tr("Hop"), tr("Type"), tr("Form"), tr("Alpha"), tr("Use at"), tr("Time"),
+                              tr("IBU"), tr("Amount"), tr("Delete"), tr("Edit") });
+
+    ui->hopsTable->setColumnCount(11);
+    ui->hopsTable->setColumnWidth(0, 150);     /* Origin	*/
+    ui->hopsTable->setColumnWidth(1, 225);     /* Hop		*/
+    ui->hopsTable->setColumnWidth(2,  75);     /* Type		*/
+    ui->hopsTable->setColumnWidth(3,  75);     /* Form          */
+    ui->hopsTable->setColumnWidth(4,  75);     /* Alpha%	*/
+    ui->hopsTable->setColumnWidth(5,  75);     /* Added         */
+    ui->hopsTable->setColumnWidth(6,  75);     /* Time		*/
+    ui->hopsTable->setColumnWidth(7,  60);     /* IBU		*/
+    ui->hopsTable->setColumnWidth(8,  75);     /* Amount	*/
+    ui->hopsTable->setColumnWidth(9,  80);     /* Delete        */
+    ui->hopsTable->setColumnWidth(10, 80);     /* Edit          */
+    ui->hopsTable->setHorizontalHeaderLabels(labels);
+    ui->hopsTable->verticalHeader()->hide();
+    ui->hopsTable->setRowCount(recipe->hops.size());
+
+    for (int i = 0; i < recipe->hops.size(); i++) {
+
+	ui->hopsTable->setItem(i, 0, new QTableWidgetItem(recipe->hops.at(i).h_origin));
+	ui->hopsTable->setItem(i, 1, new QTableWidgetItem(recipe->hops.at(i).h_name));
+
+	item = new QTableWidgetItem(h_types[recipe->hops.at(i).h_type]);
+        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+        ui->hopsTable->setItem(i, 2, item);
+
+	item = new QTableWidgetItem(h_forms[recipe->hops.at(i).h_form]);
+        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+        ui->hopsTable->setItem(i, 3, item);
+
+	item = new QTableWidgetItem(QString("%1%").arg(recipe->hops.at(i).h_alpha, 2, 'f', 1, '0'));
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->hopsTable->setItem(i, 4, item);
+
+	item = new QTableWidgetItem(h_useat[recipe->hops.at(i).h_useat]);
+        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+        ui->hopsTable->setItem(i, 5, item);
+
+	if (recipe->hops.at(i).h_useat == 2 || recipe->hops.at(i).h_useat == 4) {	// Boil or whirlpool
+	    item = new QTableWidgetItem(QString("%1 min.").arg(recipe->hops.at(i).h_time, 1, 'f', 0, '0'));
+	} else if (recipe->hops.at(i).h_useat == 5) {					// Dry-hop
+	    item = new QTableWidgetItem(QString("%1 days.").arg(recipe->hops.at(i).h_time / 1440, 1, 'f', 0, '0'));
+	} else {
+	    item = new QTableWidgetItem(QString(""));
+	}
+	item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->hopsTable->setItem(i, 6, item);
+
+	double ibu = Utils::toIBU(recipe->hops.at(i).h_useat, recipe->hops.at(i).h_form, recipe->preboil_sg, recipe->batch_size, recipe->hops.at(i).h_amount,
+	                   recipe->hops.at(i).h_time, recipe->hops.at(i).h_alpha, recipe->ibu_method, 0, recipe->hops.at(i).h_time, 0);
+	item = new QTableWidgetItem(QString("%1").arg(ibu, 2, 'f', 1, '0'));
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->hopsTable->setItem(i, 7, item);
+
+	if (recipe->hops.at(i).h_amount < 1.0) {
+	    item = new QTableWidgetItem(QString("%1 gr").arg(recipe->hops.at(i).h_amount * 1000.0, 2, 'f', 1, '0'));
+	} else {
+	    item = new QTableWidgetItem(QString("%1 kg").arg(recipe->hops.at(i).h_amount, 4, 'f', 3, '0'));
+	}
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->hopsTable->setItem(i, 8, item);
+
+	/* 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_deleteHopRow_clicked()));
+        pLayout = new QHBoxLayout(pWidget);
+        pLayout->addWidget(btn_dele);
+        pLayout->setContentsMargins(5, 0, 5, 0);
+        pWidget->setLayout(pLayout);
+        ui->hopsTable->setCellWidget(i, 9, 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_editHopRow_clicked()));
+        pLayout = new QHBoxLayout(pWidget);
+        pLayout->addWidget(btn_edit);
+        pLayout->setContentsMargins(5, 0, 5, 0);
+        pWidget->setLayout(pLayout);
+        ui->hopsTable->setCellWidget(i, 10, pWidget);
+    }
+    this->ignoreChanges = false;
+}
+
+
+void EditRecipe::on_Flavour_valueChanged(int value)
+{
+    if (value < 20) {
+        ui->hop_tasteShow->setStyleSheet(bar_20);
+	ui->hop_tasteShow->setFormat(tr("Very low"));
+    } else if (value < 40) {
+	ui->hop_tasteShow->setStyleSheet(bar_40);
+	ui->hop_tasteShow->setFormat(tr("Low"));
+    } else if (value < 60) {
+        ui->hop_tasteShow->setStyleSheet(bar_60);
+        ui->hop_tasteShow->setFormat(tr("Moderate"));
+    } else if (value < 80) {
+        ui->hop_tasteShow->setStyleSheet(bar_80);
+        ui->hop_tasteShow->setFormat(tr("High"));
+    } else {
+        ui->hop_tasteShow->setStyleSheet(bar_100);
+	ui->hop_tasteShow->setFormat(tr("Very high"));
+    }
+}
+
+
+void EditRecipe::on_Aroma_valueChanged(int value)
+{
+    if (value < 20) {
+        ui->hop_aromaShow->setStyleSheet(bar_20);
+        ui->hop_aromaShow->setFormat(tr("Very low"));
+    } else if (value < 40) {
+        ui->hop_aromaShow->setStyleSheet(bar_40);
+        ui->hop_aromaShow->setFormat(tr("Low"));
+    } else if (value < 60) {
+        ui->hop_aromaShow->setStyleSheet(bar_60);
+        ui->hop_aromaShow->setFormat(tr("Moderate"));
+    } else if (value < 80) {
+        ui->hop_aromaShow->setStyleSheet(bar_80);
+        ui->hop_aromaShow->setFormat(tr("High"));
+    } else {
+        ui->hop_aromaShow->setStyleSheet(bar_100);
+        ui->hop_aromaShow->setFormat(tr("Very high"));
+    }
+}
+
+
+void EditRecipe::calcIBUs()
+{
+    double hop_flavour = 0, hop_aroma = 0, ibus = 0;
+
+    for (int i = 0; i < recipe->hops.size(); i++) {
+
+	ibus += Utils::toIBU(recipe->hops.at(i).h_useat, recipe->hops.at(i).h_form, recipe->preboil_sg, recipe->batch_size, recipe->hops.at(i).h_amount,
+                           recipe->hops.at(i).h_time, recipe->hops.at(i).h_alpha, recipe->ibu_method, 0, recipe->hops.at(i).h_time, 0);
+	hop_flavour += Utils::hopFlavourContribution(recipe->hops.at(i).h_time, recipe->batch_size, recipe->hops.at(i).h_useat, recipe->hops.at(i).h_amount);
+        hop_aroma += Utils::hopAromaContribution(recipe->hops.at(i).h_time, recipe->batch_size, recipe->hops.at(i).h_useat, recipe->hops.at(i).h_amount);
+    }
+
+    hop_flavour = round(hop_flavour * 1000.0 / 5.0) / 10;
+    hop_aroma = round(hop_aroma * 1000.0 / 6.0) / 10;
+    if (hop_flavour > 100)
+        hop_flavour = 100;
+    if (hop_aroma > 100)
+        hop_aroma = 100;
+    qDebug() << "ibu" << recipe->est_ibu << ibus << "flavour" << hop_flavour << "aroma" << hop_aroma;
+
+    recipe->est_ibu = ibus;
+    ui->est_ibuEdit->setValue(recipe->est_ibu);
+    ui->est_ibu2Edit->setValue(recipe->est_ibu);
+    ui->hop_tasteShow->setValue(hop_flavour);
+    ui->hop_aromaShow->setValue(hop_aroma);
+}
+
+
+void EditRecipe::on_addHopRow_clicked()
+{
+    Hops newh;
+
+    qDebug() << "Add hop row";
+
+    for (int i = 0; i < recipe->hops.size(); i++) {
+        if (recipe->hops.at(i).h_amount == 0 && recipe->hops.at(i).h_alpha == 0)
+            return;     // Add only one at a time.
+    }
+
+    emit refreshAll();
+}
+
+
+void EditRecipe::on_deleteHopRow_clicked()
+{
+    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
+    int row = pb->objectName().toInt();
+    qDebug() << "Delete hop row" << row << recipe->hops.size();
+
+    if (recipe->hops.size() < 1)
+        return;
+
+    int rc = QMessageBox::warning(this, tr("Delete hop"), tr("Delete %1").arg(recipe->hops.at(row).h_name),
+                    QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
+    if (rc == QMessageBox::No)
+        return;
+
+
+}
+
+
+void EditRecipe::hop_amount_changed(double val)
+{
+    QTableWidgetItem *item;
+
+    qDebug() << "hop_amount_changed()" << recipe->hops_row << val;
+    this->ignoreChanges = true;
+
+    recipe->hops[recipe->hops_row].h_amount = val / 1000.0;
+    item = new QTableWidgetItem(QString("%1 gr").arg(val, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->hopsTable->setItem(recipe->hops_row, 8, item);
+
+    double ibu = Utils::toIBU(recipe->hops.at(recipe->hops_row).h_useat, recipe->hops.at(recipe->hops_row).h_form, recipe->preboil_sg,
+                              recipe->batch_size, recipe->hops.at(recipe->hops_row).h_amount, recipe->hops.at(recipe->hops_row).h_time,
+                              recipe->hops.at(recipe->hops_row).h_alpha, recipe->ibu_method, 0, recipe->hops.at(recipe->hops_row).h_time, 0);
+
+    ibuEdit->setValue(ibu);
+    item = new QTableWidgetItem(QString("%1").arg(ibu, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->hopsTable->setItem(recipe->hops_row, 7, item);
+
+    this->ignoreChanges = false;
+    calcIBUs();
+    is_changed();
+}
+
+
+void EditRecipe::hop_time_changed(int val)
+{
+    QTableWidgetItem *item;
+
+    qDebug() << "hop_time_changed()" << recipe->hops_row << val;
+
+    this->ignoreChanges = true;
+    recipe->hops[recipe->hops_row].h_time = val;
+
+    if (recipe->hops.at(recipe->hops_row).h_useat == 2 || recipe->hops.at(recipe->hops_row).h_useat == 4) {       // Boil or whirlpool
+        item = new QTableWidgetItem(QString("%1 min.").arg(val, 1, 'f', 0, '0'));
+    } else if (recipe->hops.at(recipe->hops_row).h_useat == 5) {                                   // Dry-hop
+        item = new QTableWidgetItem(QString("%1 days.").arg(val / 1440, 1, 'f', 0, '0'));
+    } else {
+        item = new QTableWidgetItem(QString(""));
+    }
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->hopsTable->setItem(recipe->hops_row, 6, item);
+
+    double ibu = Utils::toIBU(recipe->hops.at(recipe->hops_row).h_useat, recipe->hops.at(recipe->hops_row).h_form, recipe->preboil_sg,
+                              recipe->batch_size, recipe->hops.at(recipe->hops_row).h_amount, recipe->hops.at(recipe->hops_row).h_time,
+ 			      recipe->hops.at(recipe->hops_row).h_alpha, recipe->ibu_method, 0, recipe->hops.at(recipe->hops_row).h_time, 0);
+
+    ibuEdit->setValue(ibu);
+    item = new QTableWidgetItem(QString("%1").arg(ibu, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->hopsTable->setItem(recipe->hops_row, 7, item);
+
+    this->ignoreChanges = false;
+    calcIBUs();
+    is_changed();
+}
+
+
+void EditRecipe::hop_select_changed(int val)
+{
+    QSqlQuery query;
+    bool instock = hinstockEdit->isChecked();
+    QString w;
+    QTableWidgetItem *item;
+
+    if (val < 1)
+        return;
+
+    qDebug() << "hop_select_changed()" << recipe->fermentables_row << val << instock;
+
+    /*
+     * Search the hop pointed by the index and instock flag.
+     */
+    QString sql = "SELECT name,origin,alpha,beta,humulene,caryophyllene,cohumulone,myrcene,hsi,total_oil,type,form,cost FROM inventory_hops ";
+    if (instock)
+        sql.append("WHERE inventory > 0 ");
+    sql.append("ORDER BY origin,name");
+    query.prepare(sql);
+    query.exec();
+    query.first();
+    for (int i = 0; i < (val - 1); i++) {
+        query.next();
+    }
+    qDebug() << "found" << query.value(1).toString() << query.value(0).toString();
+
+    /*
+     * Replace the hop record contents
+     */
+    this->ignoreChanges = true;
+    recipe->hops[recipe->hops_row].h_name = query.value(0).toString();
+    recipe->hops[recipe->hops_row].h_origin = query.value(1).toString();
+    recipe->hops[recipe->hops_row].h_alpha = query.value(2).toDouble();
+    recipe->hops[recipe->hops_row].h_beta = query.value(3).toDouble();
+    recipe->hops[recipe->hops_row].h_humulene = query.value(4).toDouble();
+    recipe->hops[recipe->hops_row].h_caryophyllene = query.value(5).toDouble();
+    recipe->hops[recipe->hops_row].h_cohumulone = query.value(6).toDouble();
+    recipe->hops[recipe->hops_row].h_myrcene = query.value(7).toDouble();
+    recipe->hops[recipe->hops_row].h_hsi = query.value(8).toDouble();
+    recipe->hops[recipe->hops_row].h_total_oil = query.value(9).toDouble();
+    recipe->hops[recipe->hops_row].h_type = query.value(10).toInt();
+    recipe->hops[recipe->hops_row].h_form = query.value(11).toInt();
+    recipe->hops[recipe->hops_row].h_cost = query.value(12).toDouble();
+
+    /*
+     * Update the visible fields
+     */
+    hnameEdit->setText(recipe->hops.at(recipe->hops_row).h_name);
+    horiginEdit->setText(recipe->hops.at(recipe->hops_row).h_origin);
+
+    double ibu = Utils::toIBU(recipe->hops.at(recipe->hops_row).h_useat, recipe->hops.at(recipe->hops_row).h_form, recipe->preboil_sg,
+		              recipe->batch_size, recipe->hops.at(recipe->hops_row).h_amount, recipe->hops.at(recipe->hops_row).h_time,
+			      recipe->hops.at(recipe->hops_row).h_alpha, recipe->ibu_method, 0, recipe->hops.at(recipe->hops_row).h_time, 0);
+    ibuEdit->setValue(ibu);
+
+    ui->hopsTable->setItem(recipe->hops_row, 0, new QTableWidgetItem(recipe->hops.at(recipe->hops_row).h_origin));
+    ui->hopsTable->setItem(recipe->hops_row, 1, new QTableWidgetItem(recipe->hops.at(recipe->hops_row).h_name));
+
+    item = new QTableWidgetItem(h_types[recipe->hops.at(recipe->hops_row).h_type]);
+    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+    ui->hopsTable->setItem(recipe->hops_row, 2, item);
+
+    item = new QTableWidgetItem(h_forms[recipe->hops.at(recipe->hops_row).h_form]);
+    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+    ui->hopsTable->setItem(recipe->hops_row, 3, item);
+
+    item = new QTableWidgetItem(QString("%1%").arg(recipe->hops.at(recipe->hops_row).h_alpha, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->hopsTable->setItem(recipe->hops_row, 4, item);
+
+    item = new QTableWidgetItem(QString("%1").arg(ibu, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->hopsTable->setItem(recipe->hops_row, 7, item);
+
+    this->ignoreChanges = false;
+    calcIBUs();
+    is_changed();
+}
+
+
+void EditRecipe::hop_instock_changed(bool val)
+{
+    QSqlQuery query;
+
+    qDebug() << "hop_instock_changed()" << recipe->hops_row << val;
+
+    this->hselectEdit->setCurrentIndex(-1);
+    this->hselectEdit->clear();
+    QString sql = "SELECT origin,name,alpha,inventory FROM inventory_hops ";
+    if (val)
+        sql.append("WHERE inventory > 0 ");
+    sql.append("ORDER BY origin,name");
+    query.prepare(sql);
+    query.exec();
+    query.first();
+    this->hselectEdit->addItem("");      // Start with empty value
+    for (int i = 0; i < query.size(); i++) {
+        this->hselectEdit->addItem(query.value(0).toString()+" - "+query.value(1).toString()+" ("+query.value(2).toString()+"%) "+
+                        QString("%1 gr").arg(query.value(3).toDouble() * 1000.0, 2, 'f', 1, '0'));
+        query.next();
+    }
+}
+
+
+void EditRecipe::hop_useat_changed(int val)
+{
+    qDebug() << "hop_useat_changed()" << recipe->hops_row << val;
+
+    this->ignoreChanges = true;
+    recipe->hops[recipe->hops_row].h_useat = val;
+    QTableWidgetItem *item = new QTableWidgetItem(h_useat[val]);
+    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+    ui->hopsTable->setItem(recipe->hops_row, 5, item);
+
+    if (val == 2 || val == 4) {	// Boil or whirlpool
+	htimeLabel->setText(tr("Time in minutes:"));
+        htimeEdit->setValue(recipe->hops.at(recipe->hops_row).h_time);
+	htimeEdit->setReadOnly(false);
+    } else if (val == 5) {	// Dry-hop
+        htimeLabel->setText(tr("Time in days:"));
+	htimeEdit->setValue(recipe->hops.at(recipe->hops_row).h_time / 1440);
+	htimeEdit->setReadOnly(false);
+    } else {
+        htimeLabel->setText("");
+	htimeEdit->setValue(0);
+	htimeEdit->setReadOnly(true);
+    }
+
+    this->ignoreChanges = false;
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditRecipe::on_editHopRow_clicked()
+{
+    QSqlQuery query;
+
+    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
+    recipe->hops_row = pb->objectName().toInt();
+    qDebug() << "Edit hop row" << recipe->hops_row;
+    Hops backup = recipe->hops.at(recipe->hops_row);
+
+    QDialog* dialog = new QDialog(this);
+    dialog->resize(738, 260);
+    QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
+    buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
+    buttonBox->setGeometry(QRect(30, 210, 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 hop:"));
+    nameLabel->setGeometry(QRect(10, 10, 141, 20));
+    nameLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    QLabel *originLabel = new QLabel(dialog);
+    originLabel->setObjectName(QString::fromUtf8("originLabel"));
+    originLabel->setText(tr("Origin:"));
+    originLabel->setGeometry(QRect(10, 40, 141, 20));
+    originLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    QLabel *amountLabel = new QLabel(dialog);
+    amountLabel->setObjectName(QString::fromUtf8("amountLabel"));
+    amountLabel->setText(tr("Amount in gr:"));
+    amountLabel->setGeometry(QRect(10, 100, 141, 20));
+    amountLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    htimeLabel = new QLabel(dialog);
+    htimeLabel->setObjectName(QString::fromUtf8("htimeLabel"));
+    if (recipe->hops.at(recipe->hops_row).h_useat == 5)		// Dry-hop
+	htimeLabel->setText(tr("Time in days:"));
+    else if (recipe->hops.at(recipe->hops_row).h_useat == 2 || recipe->hops.at(recipe->hops_row).h_useat == 4)	// Boil or whirlpool
+    	htimeLabel->setText(tr("Time in minutes:"));
+    else
+	htimeLabel->setText("");
+
+    htimeLabel->setGeometry(QRect(10, 130, 141, 20));
+    htimeLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    QLabel *useatLabel = new QLabel(dialog);
+    useatLabel->setObjectName(QString::fromUtf8("useatLabel"));
+    useatLabel->setText(tr("Use at:"));
+    useatLabel->setGeometry(QRect(10, 160, 141, 20));
+    useatLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    QLabel *selectLabel = new QLabel(dialog);
+    selectLabel->setObjectName(QString::fromUtf8("selectLabel"));
+    selectLabel->setText(tr("Select hop:"));
+    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 *ibuLabel = new QLabel(dialog);
+    ibuLabel->setObjectName(QString::fromUtf8("maxLabel"));
+    ibuLabel->setText(tr("Bitterness IBU:"));
+    ibuLabel->setGeometry(QRect(420, 130, 121, 20));
+    ibuLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+
+    hselectEdit = new QComboBox(dialog);
+    hselectEdit->setObjectName(QString::fromUtf8("selectEdit"));
+    hselectEdit->setGeometry(QRect(160, 70, 371, 23));
+
+    hnameEdit = new QLineEdit(dialog);
+    hnameEdit->setObjectName(QString::fromUtf8("hnameEdit"));
+    hnameEdit->setText(recipe->hops.at(recipe->hops_row).h_name);
+    hnameEdit->setGeometry(QRect(160, 10, 511, 23));
+    hnameEdit->setReadOnly(true);
+    horiginEdit = new QLineEdit(dialog);
+    horiginEdit->setObjectName(QString::fromUtf8("horiginEdit"));
+    horiginEdit->setText(recipe->hops.at(recipe->hops_row).h_origin);
+    horiginEdit->setGeometry(QRect(160, 40, 511, 23));
+    horiginEdit->setReadOnly(true);
+    hamountEdit = new QDoubleSpinBox(dialog);
+    hamountEdit->setObjectName(QString::fromUtf8("hamountEdit"));
+    hamountEdit->setGeometry(QRect(160, 100, 121, 24));
+    hamountEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    hamountEdit->setAccelerated(true);
+    hamountEdit->setDecimals(1);
+    hamountEdit->setMaximum(1000000.0);
+    hamountEdit->setSingleStep(0.5);
+    hamountEdit->setValue(recipe->hops.at(recipe->hops_row).h_amount * 1000.0);
+    htimeEdit = new QSpinBox(dialog);
+    htimeEdit->setObjectName(QString::fromUtf8("htimeEdit"));
+    htimeEdit->setGeometry(QRect(160, 130, 121, 24));
+    htimeEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    htimeEdit->setAccelerated(true);
+    htimeEdit->setMaximum(10000.0);
+    if (recipe->hops.at(recipe->hops_row).h_useat == 2 || recipe->hops.at(recipe->hops_row).h_useat == 4) {	// Boil or whirlpool
+	htimeEdit->setValue(recipe->hops.at(recipe->hops_row).h_time);
+	htimeEdit->setReadOnly(false);
+    } else if (recipe->hops.at(recipe->hops_row).h_useat == 5){	// Dry-hop
+	htimeEdit->setValue(recipe->hops.at(recipe->hops_row).h_time / 1440);
+	htimeEdit->setReadOnly(false);
+    } else {
+	htimeEdit->setReadOnly(true);
+    }
+    useatEdit = new QComboBox(dialog);
+    useatEdit->setObjectName(QString::fromUtf8("useatEdit"));
+    useatEdit->setGeometry(QRect(160, 160, 161, 23));
+    useatEdit->addItem(tr("Mash"));
+    useatEdit->addItem(tr("First wort"));
+    useatEdit->addItem(tr("Boil"));
+    useatEdit->addItem(tr("Aroma"));
+    useatEdit->addItem(tr("Whirlpool"));
+    useatEdit->addItem(tr("Dry hop"));
+    useatEdit->setCurrentIndex(recipe->hops.at(recipe->hops_row).h_useat);
+
+    hinstockEdit = new QCheckBox(dialog);
+    hinstockEdit->setObjectName(QString::fromUtf8("hinstockEdit"));
+    hinstockEdit->setGeometry(QRect(655, 70, 85, 21));
+    hinstockEdit->setChecked(true);
+
+    ibuEdit = new QDoubleSpinBox(dialog);
+    ibuEdit->setObjectName(QString::fromUtf8("ibuEdit"));
+    ibuEdit->setGeometry(QRect(550, 130, 121, 24));
+    ibuEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    ibuEdit->setReadOnly(true);
+    ibuEdit->setButtonSymbols(QAbstractSpinBox::NoButtons);
+    ibuEdit->setDecimals(1);
+    double ibu = Utils::toIBU(recipe->hops.at(recipe->hops_row).h_useat, recipe->hops.at(recipe->hops_row).h_form, recipe->preboil_sg,
+                              recipe->batch_size, recipe->hops.at(recipe->hops_row).h_amount, recipe->hops.at(recipe->hops_row).h_time,
+                              recipe->hops.at(recipe->hops_row).h_alpha, recipe->ibu_method, 0, recipe->hops.at(recipe->hops_row).h_time, 0);
+    ibuEdit->setValue(ibu);
+
+    hop_instock_changed(true);
+
+    connect(hselectEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditRecipe::hop_select_changed);
+    connect(hamountEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditRecipe::hop_amount_changed);
+    connect(htimeEdit, QOverload<int>::of(&QSpinBox::valueChanged), this, &EditRecipe::hop_time_changed);
+    connect(useatEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditRecipe::hop_useat_changed);
+    connect(hinstockEdit, &QCheckBox::stateChanged, this, &EditRecipe::hop_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->hops[recipe->hops_row] = backup;
+    } else {
+	/* Clear time if hop is not used for boil, whirlpool or dry-hop. */
+	if (! (recipe->hops.at(recipe->hops_row).h_useat == 2 ||
+	       recipe->hops.at(recipe->hops_row).h_useat == 4 ||
+	       recipe->hops.at(recipe->hops_row).h_useat == 5)) {
+	    if (recipe->hops.at(recipe->hops_row).h_time) {
+		recipe->hops[recipe->hops_row].h_time = 0;
+		is_changed();
+	    }
+	}
+    }
+
+    disconnect(hselectEdit, nullptr, nullptr, nullptr);
+    disconnect(hamountEdit, nullptr, nullptr, nullptr);
+    disconnect(htimeEdit, nullptr, nullptr, nullptr);
+    disconnect(useatEdit, nullptr, nullptr, nullptr);
+    disconnect(hinstockEdit, nullptr, nullptr, nullptr);
+    disconnect(buttonBox, nullptr, nullptr, nullptr);
+
+    emit refreshAll();
+}
+
+

mercurial