src/EditProductTab3.cpp

Thu, 18 Aug 2022 19:47:28 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Thu, 18 Aug 2022 19:47:28 +0200
changeset 399
4b9aaf86094e
parent 397
877420a13815
child 412
5fe775f036fa
permissions
-rw-r--r--

Prompts corrections and updated translations. Minor layout changes so the prompts will fit.

/**
 * EditProduct.cpp is part of bmsapp.
 *
 * Tab 3, fermentables
 *
 * 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 EditProduct::ferment_sort_test(const Fermentables &D1, const Fermentables &D2)
{
    if (D1.added > D2.added)
	return false;
    if (D1.added < D2.added)
	return true;
    return (D1.amount >= D2.amount) && (D1.color < D2.color);
}


void EditProduct::to100Fermentables(int row)
{
    if (product->fermentables.at(row).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, 10, pWidget);
    } else {
	ui->fermentablesTable->removeCellWidget(row, 10);
    }
}


bool EditProduct::block_fermentable(int stage, int added)
{
    if (stage > PROD_STAGE_PACKAGE)
	return true;
    if (stage > PROD_STAGE_TERTIARY && added < FERMENTABLE_ADDED_BOTTLE)
	return true;
    if (stage > PROD_STAGE_PRIMARY && added < FERMENTABLE_ADDED_LAGERING)
	return true;
    if (stage > PROD_STAGE_BREW && added < FERMENTABLE_ADDED_FERMENTATION)
	return true;
    return false;
}


void EditProduct::refreshFermentables()
{
    QString w;
    QWidget* pWidget;
    QHBoxLayout* pLayout;
    QTableWidgetItem *item;

    qDebug() << "refreshFermentables" << product->fermentables.size();
    std::sort(product->fermentables.begin(), product->fermentables.end(), ferment_sort_test);

    const QStringList labels({tr("Supplier"), tr("Fermentable"), tr("EBC"), tr("Type"), tr("Graintype"), tr("When"), tr("Yield"),
		    	      tr("Amount"), tr("Stock"), tr("Procent"), tr("100%"), tr("Delete"), tr("Edit") });
    ui->fermentablesTable->setColumnCount(13);
    ui->fermentablesTable->setColumnWidth(0, 130);     /* Supplier	*/
    ui->fermentablesTable->setColumnWidth(1, 210);     /* Fermentable	*/
    ui->fermentablesTable->setColumnWidth(2,  45);     /* Color		*/
    ui->fermentablesTable->setColumnWidth(3,  75);     /* Type		*/
    ui->fermentablesTable->setColumnWidth(4,  75);     /* Graintype	*/
    ui->fermentablesTable->setColumnWidth(5,  77);     /* Added		*/
    ui->fermentablesTable->setColumnWidth(6,  55);     /* Yield		*/
    ui->fermentablesTable->setColumnWidth(7,  80);     /* Amount	*/
    ui->fermentablesTable->setColumnWidth(8,  80);     /* Stock		*/
    ui->fermentablesTable->setColumnWidth(9,  55);     /* Procent	*/
    ui->fermentablesTable->setColumnWidth(10, 50);     /* 100%		*/
    ui->fermentablesTable->setColumnWidth(11, 80);     /* Delete	*/
    ui->fermentablesTable->setColumnWidth(12, 80);     /* Edit		*/
    ui->fermentablesTable->setHorizontalHeaderLabels(labels);
    ui->fermentablesTable->verticalHeader()->hide();
    ui->fermentablesTable->setRowCount(product->fermentables.size());

    for (int i = 0; i < product->fermentables.size(); i++) {

	ui->fermentablesTable->setItem(i, 0, new QTableWidgetItem(product->fermentables.at(i).supplier));
	ui->fermentablesTable->setItem(i, 1, new QTableWidgetItem(product->fermentables.at(i).name));

        w = QString("%1").arg(product->fermentables.at(i).color, 1, 'f', 0, '0');
        item = new QTableWidgetItem(w);
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        ui->fermentablesTable->setItem(i, 2, item);

	item = new QTableWidgetItem(QCoreApplication::translate("FermentableType", g_fermentable_types[product->fermentables.at(i).type]));
	item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
	ui->fermentablesTable->setItem(i, 3, item);

        item = new QTableWidgetItem(QCoreApplication::translate("FermentableGraintype", g_fermentable_graintypes[product->fermentables.at(i).graintype]));
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        ui->fermentablesTable->setItem(i, 4, item);

        item = new QTableWidgetItem(QCoreApplication::translate("FermentableAdded", g_fermentable_added[product->fermentables.at(i).added]));
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        ui->fermentablesTable->setItem(i, 5, item);

        item = new QTableWidgetItem(QString("%1%").arg(product->fermentables.at(i).yield, 2, 'f', 1, '0'));
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        ui->fermentablesTable->setItem(i, 6, item);

        item = new QTableWidgetItem(QString("%1 Kg").arg(product->fermentables.at(i).amount, 4, 'f', 3, '0'));
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        ui->fermentablesTable->setItem(i, 7, item);

	if (block_fermentable(product->stage, product->fermentables.at(i).added)) {
	    item = new QTableWidgetItem(QString(""));
	} else {
	    item = new QTableWidgetItem(QString("%1 Kg").arg(product->fermentables.at(i).inventory, 4, 'f', 3, '0'));
            item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
	    if (product->fermentables.at(i).inventory < product->fermentables.at(i).amount)
	    	item->setForeground(QBrush(QColor(Qt::red)));
	}
        ui->fermentablesTable->setItem(i, 8, item);

	if (product->fermentables.at(i).added < FERMENTABLE_ADDED_BOTTLE) {
            item = new QTableWidgetItem(QString("%1%").arg(product->fermentables.at(i).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, 9, item);

	to100Fermentables(i);

	/* Add the Delete and Edit row buttons if allowed. */
	if (product->fermentables.at(i).added >= FERMENTABLE_ADDED_BOTTLE) {
	    item = new QTableWidgetItem("");
            item->setToolTip(tr("Edit this from the package tab"));
            ui->fermentablesTable->setItem(i, 11, item);
            item = new QTableWidgetItem("");
            item->setToolTip(tr("Edit this from the package tab"));
            ui->fermentablesTable->setItem(i, 12, item);
	} else if (block_fermentable(product->stage, product->fermentables.at(i).added)) {
	    item = new QTableWidgetItem("");
            item->setToolTip(tr("Fermentable already used"));
            ui->fermentablesTable->setItem(i, 11, item);
            item = new QTableWidgetItem("");
            item->setToolTip(tr("Fermentable already used"));
            ui->fermentablesTable->setItem(i, 12, item);
	} else {
            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(deleteFermentRow_clicked()));
            pLayout = new QHBoxLayout(pWidget);
            pLayout->addWidget(btn_dele);
            pLayout->setContentsMargins(5, 0, 5, 0);
            pWidget->setLayout(pLayout);
            ui->fermentablesTable->setCellWidget(i, 11, 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(editFermentRow_clicked()));
	    pLayout = new QHBoxLayout(pWidget);
            pLayout->addWidget(btn_edit);
	    pLayout->setContentsMargins(5, 0, 5, 0);
            pWidget->setLayout(pLayout);
            ui->fermentablesTable->setCellWidget(i, 12, pWidget);
	}
    }
}


void EditProduct::calcFermentables()
{
    int		i;
    double	psugar = 0, pcara = 0, d, s = 0, x, cw, 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 product lintner
    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
    double	bv = 0.925;		// Beer loss efficiency
    double	sr = 0.95;		// Mash and sparge efficiency

    qDebug() << "calcFermentables()";

    /*
     * Get average mashtemp and mashtime from the Mash schedule.
     * It is possible that the schedule is not (yet) present.
     */
    if (product->mashs.size() > 0) {
	for (i = 0; i < product->mashs.size(); i++) {
	    if (product->mashs.at(i).step_type == 0)		// Infusion
		mashinfuse += product->mashs.at(i).step_infuse_amount;
	    if (product->mashs.at(i).step_temp <= 75 && product->mashs.at(i).step_temp >= 60) {		// Ignore mashout and low temperatures.
		double timem = product->mashs.at(i).step_time;
		if (i > 0)
		    timem += product->mashs.at(i).ramp_time;
		mashtime += timem;
		mashtemp += timem * product->mashs.at(i).step_temp;
	    }
	}
	mashtemp = mashtemp / mashtime;
	mvol = mashinfuse;
	qDebug() << "  mash time" << mashtime << "temp" << mashtemp << "infuse" << mashinfuse;
    } else {
	qDebug() << "  no mash schedule";
    }

    const QSignalBlocker blocker1(ui->est_ogEdit);
    const QSignalBlocker blocker2(ui->est_og2Edit);

    if (product->fermentables.size() < 1) {
	qDebug() << "  no fermentables, return.";
	product->est_og = 0.980;
    	ui->est_ogEdit->setValue(0.980);
    	ui->est_og2Edit->setValue(0.980);
    	ui->est_og4Edit->setValue(0.980);
    	ui->est_ogShow->setValue(0.980);
    	product->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);
    	product->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);
    	product->est_abv = 0;
    	ui->calEdit->setValue(0);
	product->mashs_kg = 0;
	ui->mash_kgEdit->setValue(0);
	return;
    }
    qDebug() << "  adjust to 100" << product->fermentables_use100;

    product->fermentables_ok = true;
    product->mashs_kg = 0;
    for (i = 0; i < product->fermentables.size(); i++) {
	if (product->fermentables.at(i).type == FERMENTABLE_TYPE_SUGAR && product->fermentables.at(i).added < FERMENTABLE_ADDED_BOTTLE)
	    psugar += product->fermentables.at(i).percentage;
	if (product->fermentables.at(i).graintype == FERMENTABLE_GRAINTYPE_CRYSTAL && product->fermentables.at(i).added < FERMENTABLE_ADDED_BOTTLE)
	    pcara += product->fermentables.at(i).percentage;
	d = product->fermentables.at(i).amount * (product->fermentables.at(i).yield / 100) * (1 - product->fermentables.at(i).moisture / 100);
	if (product->fermentables.at(i).added == FERMENTABLE_ADDED_MASH) {
	    if (mvol > 0) {	// If mash volume is known
		mvol += product->fermentables.at(i).amount * product->fermentables.at(i).moisture / 100;
		s += d;
	    }
	    d = product->efficiency / 100 * d;
	    sugarsm += d;
	    product->mashs_kg += product->fermentables.at(i).amount;
	}
	if (product->fermentables.at(i).added == FERMENTABLE_ADDED_MASH || product->fermentables.at(i).added == FERMENTABLE_ADDED_BOIL)
	    sugarsf += d;
	if (product->fermentables.at(i).added == FERMENTABLE_ADDED_FERMENTATION || product->fermentables.at(i).added == FERMENTABLE_ADDED_LAGERING) {
	    x = (product->fermentables.at(i).yield / 100) * (1 - product->fermentables.at(i).moisture / 100);
	    addedS += product->fermentables.at(i).amount * x;
	    addedmass += product->fermentables.at(i).amount;
	    vol += (x * sugardensity + (1 - x) * 1) * product->fermentables.at(i).amount;
	}
	if (product->fermentables.at(i).added == FERMENTABLE_ADDED_MASH &&
	    (product->fermentables.at(i).type == FERMENTABLE_TYPE_GRAIN || product->fermentables.at(i).type == FERMENTABLE_TYPE_ADJUCT)) {
	    lintner += product->fermentables.at(i).diastatic_power * product->fermentables.at(i).amount;
	}
	if (product->fermentables.at(i).added < FERMENTABLE_ADDED_BOTTLE) {
	    colort += product->fermentables.at(i).amount * Utils::ebc_to_srm(product->fermentables.at(i).color);
	    colorh += product->fermentables.at(i).amount * product->fermentables.at(i).color * Utils::get_kt(product->fermentables.at(i).color);
	    colorn += (product->fermentables.at(i).percentage / 100) * product->fermentables.at(i).color;	// For 8.6 Pt wort.
	}
	/* Check supplies */
	if ((((product->inventory_reduced <= PROD_STAGE_BREW)     && (product->fermentables.at(i).added <= FERMENTABLE_ADDED_BOIL)) ||
	     ((product->inventory_reduced <= PROD_STAGE_PRIMARY)  && (product->fermentables.at(i).added == FERMENTABLE_ADDED_FERMENTATION)) ||
	     ((product->inventory_reduced <= PROD_STAGE_TERTIARY) && (product->fermentables.at(i).added == FERMENTABLE_ADDED_LAGERING)) ||
	     ((product->inventory_reduced <= PROD_STAGE_PACKAGE)  && (product->fermentables.at(i).added == FERMENTABLE_ADDED_BOTTLE)) ||
	     ((product->inventory_reduced <= PROD_STAGE_PACKAGE)  && (product->fermentables.at(i).added == FERMENTABLE_ADDED_KEGS))) &&
	       product->fermentables.at(i).inventory < product->fermentables.at(i).amount) {
	    product->fermentables_ok = false;
	}
    }
    qDebug() << "  supplies" << product->fermentables_ok;
    qDebug() << "  colort" << colort << "colorh" << colorh << "colorn" << colorn;
    qDebug() << "  psugar" << psugar << "pcara" << pcara << "mvol" << mvol;
    qDebug() << "  sugarsf" << sugarsf << "sugarsm" << sugarsm;

    double v = s / sugardensity + mvol;
    s = 1000 * s / (v * 10); //deg. Plato
    product->est_mash_sg = Utils::plato_to_sg(s);
    ui->brew_mashsgShow->setValue(product->est_mash_sg);

    /* Estimate total recipe OG */
    product->est_og = Utils::estimate_sg(sugarsf + addedS, product->batch_size);
    qDebug() << "  OG" << ui->est_ogEdit->value() << product->est_og;
    if (product->stage > PROD_STAGE_BREW) {
	ui->est_ogLabel->setText(tr("Final OG:"));
	ui->est_ogEdit->setValue(product->og);
	ui->est_ogShow->setValue(product->og);
    } else {
	ui->est_ogEdit->setValue(product->est_og);
	ui->est_ogShow->setValue(product->est_og);
    }
    ui->est_og2Edit->setValue(product->est_og);
    ui->est_og4Edit->setValue(product->est_og);

    /* Estimate SG in kettle after boil */
    product->est_og3 = Utils::estimate_sg(sugarsf, product->batch_size);
    ui->brew_aboilsgShow->setValue(product->est_og3);

    /* Estimate SG in kettle before boil */
    product->preboil_sg = Utils::estimate_sg(sugarsm, product->boil_size);
    ui->brew_preboilsgShow->setValue(product->preboil_sg);
    qDebug() << "  preboil SG" << product->preboil_sg;

    /*
     * Recalculate volumes
     */
    double aboil_volume = product->batch_size;
    if (product->brew_aboil_volume > 0)
	aboil_volume = product->brew_aboil_volume / 1.04;	// Volume @ 20 degrees.
    if (product->brew_fermenter_tcloss == 0) {
	product->brew_fermenter_tcloss = product->eq_trub_chiller_loss;
	ui->brew_trublossEdit->setValue(product->brew_fermenter_tcloss);
    }
    product->brew_fermenter_volume = aboil_volume - product->brew_fermenter_tcloss + product->brew_fermenter_extrawater;
    ui->brew_tofermentEdit->setValue(product->brew_fermenter_volume);
    /* Calculate SG in fermenter */
    double ogx = product->brew_aboil_sg;
    if (ogx < 1.002)
	ogx = product->est_og3;
    double topw = product->brew_fermenter_extrawater;

    if (product->brew_fermenter_volume > 0) {
	double sug = Utils::sg_to_plato(ogx) * product->brew_fermenter_volume * ogx / 100;  //kg of sugar in
   	sug += addedS; //kg

	if ((product->brew_fermenter_volume * ogx + addedmass) > 0) {
	    double pt = 100 * sug / (product->brew_fermenter_volume * ogx + addedmass + topw);
	    product->og = product->brew_fermenter_sg = round(Utils::plato_to_sg(pt) * 10000) / 10000.0;
	    ui->brew_fermentsgShow->setValue(product->brew_fermenter_sg);
	    // color
	    if (product->color_method == 4) {		// Naudts
		product->brew_fermenter_color = round(((pt / 8.6) * colorn) + (product->boil_time / 60));
	    } else if (product->color_method == 3) {	// Hans Halberstadt
		product->brew_fermenter_color = round((4.46 * bv * sr) / (aboil_volume + topw) * colorh);
	    } else {
		cw = colort / (aboil_volume + topw) * 8.34436;
		product->brew_fermenter_color = Utils::kw_to_ebc(product->color_method, cw);
	    }
	    ui->brew_fermentcolorShow->setValue(product->brew_fermenter_color);
	    ui->brew_fermentcolorShow->setStyleSheet(Utils::ebc_to_style(product->brew_fermenter_color));
	}
    } else {
	// Negative volume
	product->brew_fermenter_sg = product->brew_fermenter_color = 0;
	ui->brew_fermentsgShow->setValue(0);
	ui->brew_fermentcolorShow->setStyleSheet(0);
	ui->brew_fermentcolorShow->setValue(0);
    }

    /*
     * Color of the wort
     */
    if (product->color_method == 4) {				// Naudts
	color = round(((Utils::sg_to_plato(product->est_og) / 8.6) * colorn) + (product->boil_time / 60));
    } else if (product->color_method == 3) {			// Hans Halberstadt
	color = round((4.46 * bv * sr) / product->batch_size * colorh);
    } else {
	cw = colort / product->batch_size * 8.34436;
	color = Utils::kw_to_ebc(product->color_method, cw);
	//qDebug() << "  oud EBC" << color << "new EBC" << Utils::kw_to_newebc(product->color_method, cw) << "SRM" << Utils::kw_to_srm(product->color_method, cw);
    }
    qDebug() << "  color" << ui->est_colorEdit->value() << color << product->est_color;
    product->est_color = color;
    ui->est_color2Edit->setValue(color);
    ui->est_color2Edit->setStyleSheet(Utils::ebc_to_style(color));
    if (product->stage > PROD_STAGE_BREW) {
        ui->est_colorLabel->setText(tr("Final EBC:"));
	ui->est_colorEdit->setValue(product->brew_fermenter_color);
	ui->est_colorEdit->setStyleSheet(Utils::ebc_to_style(product->brew_fermenter_color));
	ui->est_colorShow->setValue(product->brew_fermenter_color);
    } else {
	ui->est_colorEdit->setValue(color);
	ui->est_colorEdit->setStyleSheet(Utils::ebc_to_style(color));
	ui->est_colorShow->setValue(color);
    }

    if (round(product->mashs_kg / product->eq_mash_max * 100) > 120)
	ui->perc_mashShow->setValue(120);
    else
	ui->perc_mashShow->setValue(round(product->mashs_kg / product->eq_mash_max * 100));
    ui->mash_kgEdit->setValue(product->mashs_kg);
    ui->perc_sugarsShow->setValue(round(psugar));
    ui->perc_caraShow->setValue(round(pcara));
    if (product->mashs_kg > 0) {
	qDebug() << "  lintner" << lintner << "  mashkg" << product->mashs_kg << "final" << round(lintner / product->mashs_kg);
	ui->lintnerShow->setValue(round(lintner / product->mashs_kg));
    } else {
	qDebug() << "  lintner N/A";
	ui->lintnerShow->setValue(0);
    }

    /*
     * Calculate the apparant attenuation.
     */
    double svg = 0;
    double initcells = 0;
    product->yeasts_ok = true;
    if (product->yeasts.size() > 0) {
        for (i = 0; i < product->yeasts.size(); i++) {
	    if (product->yeasts.at(i).use == 0) {		// Used in primary
		if (product->yeasts.at(i).attenuation > svg)
		    svg = product->yeasts.at(i).attenuation;	// Take the highest if multiple yeasts.
	    }
	    if (product->yeasts.at(i).form == 0)
		initcells += (product->yeasts.at(i).cells / 1000000000) * product->yeasts.at(i).amount * (product->starter_viability / 100);
	    else
		initcells += (product->yeasts.at(i).cells / 1000000) * product->yeasts.at(i).amount * (product->starter_viability / 100);
	    // TODO: brett or others in secondary.
	    if ((((product->inventory_reduced <= PROD_STAGE_PRIMARY)   && (product->yeasts.at(i).use == 0)) ||  // Primary
        	 ((product->inventory_reduced <= PROD_STAGE_SECONDARY) && (product->yeasts.at(i).use == 1)) ||  // Secondary
        	 ((product->inventory_reduced <= PROD_STAGE_TERTIARY)  && (product->yeasts.at(i).use == 2)) ||  // Tertiary
        	 ((product->inventory_reduced <= PROD_STAGE_PACKAGE)   && (product->yeasts.at(i).use == 3))) && // Bottle
        	  (product->yeasts.at(i).inventory < product->yeasts.at(i).amount)) {
		product->yeasts_ok = false;
	    }
	}
	qDebug() << "  est SVG" << svg;
    }
    if (svg == 0)
	svg = 77.0;
    ui->est_svgEdit->setValue(svg);

    if (product->mashs_kg > 0 && mashinfuse > 0 && mashtime > 0 && mashtemp > 0)
	product->est_fg = Utils::estimate_fg(psugar, pcara, mashinfuse / product->mashs_kg, mashtime, mashtemp, svg, product->est_og);
    else
	product->est_fg = Utils::estimate_fg(psugar, pcara, 0, 0, 0, svg, product->est_og);
    qDebug() << "  est FG" << ui->est_fgEdit->value() << product->est_fg;
    product->est_abv = Utils::abvol(product->est_og, product->est_fg);
    qDebug() << "  est ABV" << ui->est_abvEdit->value() << product->est_abv;

    if (product->stage > PROD_STAGE_TERTIARY) {
	ui->est_fgLabel->setText(tr("Final FG:"));
	ui->est_fgEdit->setValue(product->fg);
	ui->est_fgShow->setValue(product->fg);
	ui->est_abvLabel->setText(tr("Final ABV:"));
	double abv = Utils::abvol(product->og, product->fg);
	ui->est_abvEdit->setValue(abv);
	ui->est_abvShow->setValue(abv);
    } else {
	ui->est_fgEdit->setValue(product->est_fg);
	ui->est_fgShow->setValue(product->est_fg);
	ui->est_abvEdit->setValue(product->est_abv);
	ui->est_abvShow->setValue(product->est_abv);
    }
    ui->est_fg3Edit->setValue(product->est_fg);
    ui->est_abv2Edit->setValue(product->est_abv);

    /*
     * Calculate kilocalories/liter. Formula from brouwhulp.
     * Take the alcohol and sugar parts and then combine.
     */
    double alc, sug;
    if (product->stage > PROD_STAGE_TERTIARY) {
	alc = 1881.22 * product->fg * (product->og - product->fg) / (1.775 - product->og);
	sug = 3550 * product->fg * (0.1808 * product->og + 0.8192 * product->fg - 1.0004);
    } else {
	alc = 1881.22 * product->est_fg * (product->est_og - product->est_fg) / (1.775 - product->est_og);
	sug = 3550 * product->est_fg * (0.1808 * product->est_og + 0.8192 * product->est_fg - 1.0004);
    }
    ui->calEdit->setValue(round((alc + sug) / (12 * 0.0295735296)));
}


void EditProduct::calcFermentablesFromOG(double og)
{
    qDebug() << "calcFermentablesFromOG" << og;

    int    i;
    double totmass = 0;
    double tot = 0;
    double d, amount;
    double efficiency = product->efficiency;
    double sug = Utils::sg_to_plato(og) * product->batch_size * og / 100.0;	// total amount of sugars in kg.

    for (i = 0; i < product->fermentables.size(); i++) {
	if (product->fermentables.at(i).added < FERMENTABLE_ADDED_BOTTLE) {
	    d = product->fermentables.at(i).percentage / 100.0 *
		    (product->fermentables.at(i).yield / 100.0) *
		    (1 - product->fermentables.at(i).moisture / 100.0);
	    if (product->fermentables.at(i).added == FERMENTABLE_ADDED_MASH)
		d = efficiency / 100.0 * d;
	    tot += d;
	}
    }
    if (tot)
	totmass = round((sug / tot) * 1000.0) / 1000.0;

    if (totmass) {
	for (i = 0; i < product->fermentables.size(); i++) {
	    amount = round(product->fermentables.at(i).percentage * 10.0 * totmass) / 1000.0;
	    product->fermentables[i].amount = amount;
	}
    }
}


void EditProduct::ferment_perc_mash_valueChanged(int value)
{
    if (value < 90) {
	ui->perc_mashShow->setStyleSheet(bar_green);
    } else {
	double s1 = 90.0 / value;
	if (value < 100) {
	    ui->perc_mashShow->setStyleSheet(QString("QProgressBar::chunk {background-color: qlineargradient(x0: 0, x2: 1, "
                  "stop: 0 green, stop: %1 green, stop: %2 orange, stop: 1 orange"
                  ");}").arg(s1 - 0.00001).arg(s1));
	} else {
	    double s2 = 100.0 / value;
	    ui->perc_mashShow->setStyleSheet(QString("QProgressBar::chunk {background-color: qlineargradient(x0: 0, x2: 1, "
                  "stop: 0 green, stop: %1 green, stop: %2 orange, stop: %3 orange, stop: %4 red, stop: 1 red"
                  ");}").arg(s1 - 0.00003).arg(s1 - 0.00002).arg(s2 - 0.00001).arg(s2));
	}
    }
}


void EditProduct::ferment_perc_sugars_valueChanged(int value)
{
    if (value < 20)
	ui->perc_sugarsShow->setStyleSheet(bar_green);
    else {
	double s1 = 20.0 / value;
        ui->perc_sugarsShow->setStyleSheet(QString("QProgressBar::chunk {background-color: qlineargradient(x0: 0, x2: 1, "
                  "stop: 0 green, stop: %1 green, stop: %2 red, stop: 1 red);}").arg(s1 - 0.00001).arg(s1));
    }
}


void EditProduct::ferment_perc_cara_valueChanged(int value)
{
    if (value < 25)
	ui->perc_caraShow->setStyleSheet(bar_green);
    else {
	double s1 = 25.0 / value;
    	ui->perc_caraShow->setStyleSheet(QString("QProgressBar::chunk {background-color: qlineargradient(x0: 0, x2: 1, "
                  "stop: 0 green, stop: %1 green, stop: %2 red, stop: 1 red);}").arg(s1 - 0.00001).arg(s1));
    }
}


void EditProduct::ferment_lintner_valueChanged(int value)
{
    /*
     * Calculate the color stop positions
     */
    if (value < 30) {
	ui->lintnerShow->setStyleSheet(bar_red);
    } else {
	double s1 = 30.0 / value;
	if (value < 40) {
	    ui->lintnerShow->setStyleSheet(QString("QProgressBar::chunk {background-color: qlineargradient(x0: 0, x2: 1, "
                  "stop: 0 red, stop: %1 red, stop: %2 orange, stop: 1 orange);}").arg(s1 - 0.00001).arg(s1));
	} else {
	    double s2 = 40.0 / value;
	    ui->lintnerShow->setStyleSheet(QString("QProgressBar::chunk {background-color: qlineargradient(x0: 0, x2: 1, "
	          "stop: 0 red, stop: %1 red, stop: %2 orange, stop: %3 orange, stop: %4 green, stop: 1 green"
                  ");}").arg(s1 - 0.00003).arg(s1 - 0.00002).arg(s2 - 0.00001).arg(s2));
	}
    }
}


void EditProduct::addFermentRow_clicked()
{
    Fermentables newf;

    qDebug() << "Add fermentable row";

    for (int i = 0; i < product->fermentables.size(); i++) {
	if (product->fermentables.at(i).amount == 0 && product->fermentables.at(i).color == 0)
	    return;	// Add only one at a time.
    }

    newf.name = "Select one";
    newf.origin = "";
    newf.supplier = "";
    newf.amount = 0;
    newf.cost = 0;
    newf.type = FERMENTABLE_TYPE_GRAIN;
    newf.yield = 0;
    newf.color = 0;
    newf.coarse_fine_diff = 0;
    newf.moisture = 0;
    newf.diastatic_power = 0;
    newf.protein = 0;
    newf.dissolved_protein = 0;
    newf.max_in_batch = 100;
    newf.graintype = FERMENTABLE_GRAINTYPE_BASE;
    newf.added = FERMENTABLE_ADDED_MASH;
    newf.recommend_mash = true;
    newf.add_after_boil = false;
    newf.adjust_to_total_100 = false;
    newf.percentage = 0;
    newf.di_ph = 0;
    newf.acid_to_ph_57 = 0;

    product->fermentables.append(newf);
    emit refreshAll();
}


void EditProduct::deleteFermentRow_clicked()
{
    if (product->locked)
	return;

    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
    int row = pb->objectName().toInt();
    qDebug() << "Delete fermentable row" << row << product->fermentables.size();

    if (product->fermentables.size() < 1)
	return;

    int rc = QMessageBox::warning(this, tr("Delete fermentable"), tr("Delete %1").arg(product->fermentables.at(row).name),
		    QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
    if (rc == QMessageBox::No)
	return;

    product->fermentables.removeAt(row);

    /*
     * Recalculate the percentages on the rows left.
     */
    double total = 0;
    for (int i = 0; i < product->fermentables.size(); i++)
        if (product->fermentables.at(i).added < FERMENTABLE_ADDED_BOTTLE)	// Only before bottle/kegging
            total += product->fermentables.at(i).amount;
    for (int i = 0; i < product->fermentables.size(); i++)
        if (product->fermentables.at(i).added < FERMENTABLE_ADDED_BOTTLE)
            product->fermentables[i].percentage = product->fermentables.at(i).amount / total * 100;

    is_changed();
    emit refreshAll();
}


void EditProduct::ferment_amount_changed(double val)
{
    QTableWidgetItem *item;
    double	total = 0, perc;

    if (product->fermentables_use100 && product->fermentables.at(product->fermentables_row).added < FERMENTABLE_ADDED_BOTTLE)
	return;

    qDebug() << "ferment_amount_changed()" << product->fermentables_row << val;

    product->fermentables[product->fermentables_row].amount = val;
    item = new QTableWidgetItem(QString("%1 Kg").arg(val, 4, 'f', 3, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->fermentablesTable->setItem(product->fermentables_row, 7, item);

    for (int i = 0; i < product->fermentables.size(); i++)
	if (product->fermentables.at(i).added < FERMENTABLE_ADDED_BOTTLE)	// Only before bottle/kegging
	    total += product->fermentables.at(i).amount;
    /*
     * Recalculate the percentages
     */
    for (int i = 0; i < product->fermentables.size(); i++) {
	if (product->fermentables.at(i).added < FERMENTABLE_ADDED_BOTTLE) {
	    perc = product->fermentables.at(i).amount / total * 100;
	    product->fermentables[i].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 == product->fermentables_row)
	    	this->pctEdit->setValue(perc);
	}
    }
    is_changed();
}


void EditProduct::ferment_pct_changed(double val)
{
    QTableWidgetItem *item;
    double	total = 0, row100 = -1;

    if (! product->fermentables_use100)
        return;

    qDebug() << "ferment_pct_changed()" << product->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 < product->fermentables.size(); i++) {
        if (product->fermentables.at(i).added < FERMENTABLE_ADDED_BOTTLE)	// Only before bottle/kegging
            total += product->fermentables.at(i).amount;
	if (product->fermentables.at(i).adjust_to_total_100)
	    row100 = i;
    }
    double oldperc = product->fermentables.at(product->fermentables_row).percentage;
    double diffp = val - oldperc;
    double diffw = (diffp / 100) * total;
    qDebug() << "row100" << row100 << "total" << total << "diff kg" << diffw << "diff %" << diffp;

    product->fermentables[product->fermentables_row].percentage += diffp;
    product->fermentables[product->fermentables_row].amount += diffw;
    product->fermentables[row100].percentage -= diffp;
    product->fermentables[row100].amount -= diffw;

    item = new QTableWidgetItem(QString("%1 Kg").arg(product->fermentables[product->fermentables_row].amount, 4, 'f', 3, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->fermentablesTable->setItem(product->fermentables_row, 7, item);
    this->famountEdit->setValue(product->fermentables[product->fermentables_row].amount);

    item = new QTableWidgetItem(QString("%1%").arg(product->fermentables[product->fermentables_row].percentage, 2, 'f', 1, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->fermentablesTable->setItem(product->fermentables_row, 8, item);

    item = new QTableWidgetItem(QString("%1 Kg").arg(product->fermentables[row100].amount, 4, 'f', 3, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->fermentablesTable->setItem(row100, 7, item);

    item = new QTableWidgetItem(QString("%1%").arg(product->fermentables[row100].percentage, 2, 'f', 1, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->fermentablesTable->setItem(row100, 8, item);

    is_changed();
}


void EditProduct::ferment_to100_changed(bool val)
{
    qDebug() << "ferment_to100_changed()" << product->fermentables_row << val << product->fermentables_use100;

    if (product->fermentables.at(product->fermentables_row).added >= FERMENTABLE_ADDED_BOTTLE) {
	const QSignalBlocker blocker1(to100Edit);
	product->fermentables[product->fermentables_row].adjust_to_total_100 = false;
	to100Edit->setChecked(false);
	return;
    }

    /*
     * 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 (! product->fermentables_use100 && val) {
	/* Scenario 1. */
        product->fermentables_use100 = true;
        product->fermentables[product->fermentables_row].adjust_to_total_100 = true;
	pctEdit->setReadOnly(false);
	famountEdit->setReadOnly(true);
    } else if (product->fermentables_use100 && product->fermentables[product->fermentables_row].adjust_to_total_100 && ! val) {
	/* Scenario 2. */
	product->fermentables[product->fermentables_row].adjust_to_total_100 = false;
	product->fermentables_use100 = false;
	pctEdit->setReadOnly(true);
	famountEdit->setReadOnly(false);
    } else if (product->fermentables_use100 && ! product->fermentables[product->fermentables_row].adjust_to_total_100 && val) {
	/* Scenario 3. */
	for (int i = 0; i < product->fermentables.size(); i++) {
	    product->fermentables[i].adjust_to_total_100 = false;
	}
	product->fermentables[product->fermentables_row].adjust_to_total_100 = true;
    } else {
	qDebug() << "bug";
	return;
    }

    for (int i = 0; i < product->fermentables.size(); i++) {
	to100Fermentables(i);
    }
    is_changed();
}


void EditProduct::ferment_select_changed(int val)
{
    QSqlQuery query;
    bool instock = finstockEdit->isChecked();
    QString w;
    QTableWidgetItem *item;

    if (val < 1)
	return;

    qDebug() << "ferment_select_changed()" << product->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,inventory 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();
    }

    /*
     * Replace the fermentable record contents
     */
    product->fermentables[product->fermentables_row].name = query.value(0).toString();
    product->fermentables[product->fermentables_row].origin = query.value(1).toString();
    product->fermentables[product->fermentables_row].supplier = query.value(2).toString();
    product->fermentables[product->fermentables_row].cost = query.value(3).toDouble();
    product->fermentables[product->fermentables_row].type = query.value(4).toInt();
    product->fermentables[product->fermentables_row].yield = query.value(5).toDouble();
    product->fermentables[product->fermentables_row].color = query.value(6).toDouble();
    product->fermentables[product->fermentables_row].coarse_fine_diff = query.value(7).toDouble();
    product->fermentables[product->fermentables_row].moisture = query.value(8).toDouble();
    product->fermentables[product->fermentables_row].diastatic_power = query.value(9).toDouble();
    product->fermentables[product->fermentables_row].protein = query.value(10).toDouble();
    product->fermentables[product->fermentables_row].dissolved_protein = query.value(11).toDouble();
    product->fermentables[product->fermentables_row].max_in_batch = query.value(12).toDouble();
    product->fermentables[product->fermentables_row].graintype = query.value(13).toInt();
    product->fermentables[product->fermentables_row].recommend_mash = query.value(14).toInt() ? true:false;
    product->fermentables[product->fermentables_row].add_after_boil = query.value(15).toInt() ? true:false;
    product->fermentables[product->fermentables_row].di_ph = query.value(16).toDouble();
    product->fermentables[product->fermentables_row].acid_to_ph_57 = query.value(17).toDouble();
    product->fermentables[product->fermentables_row].inventory = query.value(18).toDouble();

    /*
     * Update the visible fields
     */
    fnameEdit->setText(product->fermentables.at(product->fermentables_row).name);
    fsupplierEdit->setText(product->fermentables.at(product->fermentables_row).supplier);
    fmaxEdit->setValue(product->fermentables.at(product->fermentables_row).max_in_batch);

    ui->fermentablesTable->setItem(product->fermentables_row, 0, new QTableWidgetItem(product->fermentables.at(product->fermentables_row).supplier));
    ui->fermentablesTable->setItem(product->fermentables_row, 1, new QTableWidgetItem(product->fermentables.at(product->fermentables_row).name));

    w = QString("%1").arg(product->fermentables.at(product->fermentables_row).color, 1, 'f', 0, '0');
    item = new QTableWidgetItem(w);
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->fermentablesTable->setItem(product->fermentables_row, 2, item);

    item = new QTableWidgetItem(QCoreApplication::translate("FermentableType", g_fermentable_types[product->fermentables.at(product->fermentables_row).type]));
    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
    ui->fermentablesTable->setItem(product->fermentables_row, 3, item);

    item = new QTableWidgetItem(QCoreApplication::translate("FermentableGraintype", g_fermentable_graintypes[product->fermentables.at(product->fermentables_row).graintype]));
    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
    ui->fermentablesTable->setItem(product->fermentables_row, 4, item);

    item = new QTableWidgetItem(QString("%1%").arg(product->fermentables.at(product->fermentables_row).yield, 2, 'f', 1, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->fermentablesTable->setItem(product->fermentables_row, 6, item);

    item = new QTableWidgetItem(QString("%1 Kg").arg(product->fermentables.at(product->fermentables_row).inventory, 4, 'f', 3, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    if (product->fermentables.at(product->fermentables_row).inventory < product->fermentables.at(product->fermentables_row).amount)
	item->setForeground(QBrush(QColor(Qt::red)));
    ui->fermentablesTable->setItem(product->fermentables_row, 8, item);

    calcFermentables();
    is_changed();
}


void EditProduct::ferment_instock_changed(bool val)
{
    QSqlQuery query;

    qDebug() << "ferment_instock_changed()" << product->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 EditProduct::ferment_added_changed(int val)
{
    qDebug() << "ferment_added_changed()" << product->fermentables_row << val;

    product->fermentables[product->fermentables_row].added = val;
    QTableWidgetItem *item = new QTableWidgetItem(QCoreApplication::translate("FermentableAdded", g_fermentable_added[val]));
    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
    ui->fermentablesTable->setItem(product->fermentables_row, 5, item);

    famountEdit->setReadOnly(product->fermentables_use100 && product->fermentables.at(product->fermentables_row).added < FERMENTABLE_ADDED_BOTTLE);
    pctEdit->setReadOnly(! (product->fermentables_use100 && product->fermentables.at(product->fermentables_row).added < FERMENTABLE_ADDED_BOTTLE));

    double total = 0;
    for (int i = 0; i < product->fermentables.size(); i++)
        if (product->fermentables.at(i).added < FERMENTABLE_ADDED_BOTTLE)	// Only before bottle/kegging
            total += product->fermentables.at(i).amount;
    for (int i = 0; i < product->fermentables.size(); i++)
        if (product->fermentables.at(i).added < FERMENTABLE_ADDED_BOTTLE)
            product->fermentables[i].percentage = product->fermentables.at(i).amount / total * 100;

    is_changed();
    emit refreshAll();
}


void EditProduct::editFermentRow_clicked()
{
    QSqlQuery query;

    if (product->locked)
	return;

    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
    product->fermentables_row = pb->objectName().toInt();
    qDebug() << "Edit fermentable row" << product->fermentables_row;
    Fermentables backup = product->fermentables.at(product->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(product->fermentables.at(product->fermentables_row).name);
    fnameEdit->setGeometry(QRect(160, 10, 511, 23));
    fnameEdit->setReadOnly(true);
    fsupplierEdit = new QLineEdit(dialog);
    fsupplierEdit->setObjectName(QString::fromUtf8("fsupplierEdit"));
    fsupplierEdit->setText(product->fermentables.at(product->fermentables_row).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(product->fermentables_use100 && product->fermentables.at(product->fermentables_row).added < 4);
    famountEdit->setMaximum(100000.0);
    famountEdit->setSingleStep(0.0010);
    famountEdit->setValue(product->fermentables.at(product->fermentables_row).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 (product->fermentables_use100 && product->fermentables.at(product->fermentables_row).added < FERMENTABLE_ADDED_BOTTLE) {
    	if (product->fermentables.at(product->fermentables_row).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(product->fermentables.at(product->fermentables_row).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->setCurrentIndex(product->fermentables.at(product->fermentables_row).added);

    to100Edit = new QCheckBox(dialog);
    to100Edit->setObjectName(QString::fromUtf8("to100Edit"));
    to100Edit->setGeometry(QRect(160, 160, 85, 21));
    to100Edit->setChecked(product->fermentables.at(product->fermentables_row).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(product->fermentables.at(product->fermentables_row).max_in_batch);

    ferment_instock_changed(true);

    connect(fselectEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::ferment_select_changed);
    connect(famountEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::ferment_amount_changed);
    connect(pctEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::ferment_pct_changed);
    connect(faddedEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::ferment_added_changed);
    connect(to100Edit, &QCheckBox::stateChanged, this, &EditProduct::ferment_to100_changed);
    connect(finstockEdit, &QCheckBox::stateChanged, this, &EditProduct::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";
	product->fermentables[product->fermentables_row] = backup;
	/*
	 * Recalculate the percentages
	 */
	double total = 0;
	for (int i = 0; i < product->fermentables.size(); i++)
	    if (product->fermentables.at(i).added < FERMENTABLE_ADDED_BOTTLE)	// Only before bottle/kegging
		total += product->fermentables.at(i).amount;
	product->fermentables_use100 = false;
	for (int i = 0; i < product->fermentables.size(); i++) {
	    if (product->fermentables.at(i).adjust_to_total_100)
	    	product->fermentables_use100 = true;
	    if (product->fermentables.at(i).added < FERMENTABLE_ADDED_BOTTLE) {
		product->fermentables[i].percentage = product->fermentables.at(i).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();
}

mercurial