src/EditProductTab3.cpp

Fri, 20 May 2022 20:43:33 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Fri, 20 May 2022 20:43:33 +0200
changeset 225
448e4187cada
parent 224
d369948a3eb5
child 229
815ee118ad49
permissions
-rw-r--r--

Implemented the tasting tab. On the generic tab, show the ingredients check or the read only prompt on the same place depending on the product stage. Fixed the yeasts ingredients in stock check. Reordered the tab order of all edit fields. It looks like this module is ready and needs testing.

/**
 * 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.f_added > D2.f_added)
	return false;
    if (D1.f_added < D2.f_added)
	return true;
    return (D1.f_amount >= D2.f_amount) && (D1.f_color < D2.f_color);
}


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


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).f_supplier));
	ui->fermentablesTable->setItem(i, 1, new QTableWidgetItem(product->fermentables.at(i).f_name));

        w = QString("%1").arg(product->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(fermentable_types[product->fermentables.at(i).f_type]);
	item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
	ui->fermentablesTable->setItem(i, 3, item);

        item = new QTableWidgetItem(fermentable_graintypes[product->fermentables.at(i).f_graintype]);
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        ui->fermentablesTable->setItem(i, 4, item);

        item = new QTableWidgetItem(fermentable_added[product->fermentables.at(i).f_added]);
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        ui->fermentablesTable->setItem(i, 5, item);

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

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

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

	to100Fermentables(i);

	/* Add the Delete row button */
	if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE) {
            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);
	} else {
	    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);
	}
    }
}


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) {		// Ignore mashout
		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;
	    }
	}
	mashtime += 5;	// Correction for missing last ramp_time.
	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).f_type == FERMENTABLE_TYPE_SUGAR && product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE)
	    psugar += product->fermentables.at(i).f_percentage;
	if (product->fermentables.at(i).f_graintype == FERMENTABLE_GRAINTYPE_CRYSTAL && product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE)
	    pcara += product->fermentables.at(i).f_percentage;
	d = product->fermentables.at(i).f_amount * (product->fermentables.at(i).f_yield / 100) * (1 - product->fermentables.at(i).f_moisture / 100);
	if (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_MASH) {
	    if (mvol > 0) {	// If mash volume is known
		mvol += product->fermentables.at(i).f_amount * product->fermentables.at(i).f_moisture / 100;
		s += d;
	    }
	    d = product->efficiency / 100 * d;
	    sugarsm += d;
	    product->mashs_kg += product->fermentables.at(i).f_amount;
	}
	if (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_MASH || product->fermentables.at(i).f_added == FERMENTABLE_ADDED_BOIL)
	    sugarsf += d;
	if (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_FERMENTATION || product->fermentables.at(i).f_added == FERMENTABLE_ADDED_LAGERING) {
	    x = (product->fermentables.at(i).f_yield / 100) * (1 - product->fermentables.at(i).f_moisture / 100);
	    addedS += product->fermentables.at(i).f_amount * x;
	    addedmass += product->fermentables.at(i).f_amount;
	    vol += (x * sugardensity + (1 - x) * 1) * product->fermentables.at(i).f_amount;
	}
	if (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_MASH &&
	    (product->fermentables.at(i).f_type == FERMENTABLE_TYPE_GRAIN || product->fermentables.at(i).f_type == FERMENTABLE_TYPE_ADJUCT) &&
	    product->fermentables.at(i).f_color < 50) {
	    lintner += product->fermentables.at(i).f_diastatic_power * product->fermentables.at(i).f_amount;
	}
	if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE) {
	    colort += product->fermentables.at(i).f_amount * Utils::ebc_to_srm(product->fermentables.at(i).f_color);
	    colorh += product->fermentables.at(i).f_amount * product->fermentables.at(i).f_color * Utils::get_kt(product->fermentables.at(i).f_color);
	    colorn += (product->fermentables.at(i).f_percentage / 100) * product->fermentables.at(i).f_color;	// For 8.6 Pt wort.
	}
	/* Check supplies */
	if ((((product->inventory_reduced <= PROD_STAGE_BREW)     && (product->fermentables.at(i).f_added <= FERMENTABLE_ADDED_BOIL)) ||
	     ((product->inventory_reduced <= PROD_STAGE_PRIMARY)  && (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_FERMENTATION)) ||
	     ((product->inventory_reduced <= PROD_STAGE_TERTIARY) && (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_LAGERING)) ||
	     ((product->inventory_reduced <= PROD_STAGE_PACKAGE)  && (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_BOTTLE)) ||
	     ((product->inventory_reduced <= PROD_STAGE_PACKAGE)  && (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_KEGS))) &&
	       product->fermentables.at(i).f_inventory < product->fermentables.at(i).f_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;
    ui->est_ogEdit->setValue(product->est_og);
    ui->est_og2Edit->setValue(product->est_og);
    ui->est_og4Edit->setValue(product->est_og);
    ui->est_ogShow->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_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);

    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).y_use == 0) {		// Used in primary
		if (product->yeasts.at(i).y_attenuation > svg)
		    svg = product->yeasts.at(i).y_attenuation;	// Take the highest if multiple yeasts.
	    }
	    if (product->yeasts.at(i).y_form == 0)
		initcells += (product->yeasts.at(i).y_cells / 1000000000) * product->yeasts.at(i).y_amount * (product->starter_viability / 100);
	    else
		initcells += (product->yeasts.at(i).y_cells / 1000000) * product->yeasts.at(i).y_amount * (product->starter_viability / 100);
	    // TODO: brett or others in secondary.
	    if ((((product->inventory_reduced <= PROD_STAGE_PRIMARY)   && (product->yeasts.at(i).y_use == 0)) ||  // Primary
        	 ((product->inventory_reduced <= PROD_STAGE_SECONDARY) && (product->yeasts.at(i).y_use == 1)) ||  // Secondary
        	 ((product->inventory_reduced <= PROD_STAGE_TERTIARY)  && (product->yeasts.at(i).y_use == 2)) ||  // Tertiary
        	 ((product->inventory_reduced <= PROD_STAGE_PACKAGE)   && (product->yeasts.at(i).y_use == 3))) && // Bottle
        	  (product->yeasts.at(i).y_inventory < product->yeasts.at(i).y_amount)) {
		product->yeasts_ok = false;
	    }
	}
	qDebug() << "  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() << "  FG" << ui->est_fgEdit->value() << product->est_fg;
    ui->est_fgEdit->setValue(product->est_fg);
    ui->est_fg3Edit->setValue(product->est_fg);
    ui->est_fgShow->setValue(product->est_fg);

    /*
     * Calculate the final values if available.
     */
    if ((product->stage >= PROD_STAGE_PACKAGE) && (product->fg > 0.990) && (product->fg < product->brew_fermenter_sg)) {

    }

    product->est_abv = Utils::abvol(product->est_og, product->est_fg);
    qDebug() << "  ABV" << ui->est_abvEdit->value() << product->est_abv;
    ui->est_abvEdit->setValue(product->est_abv);
    ui->est_abv2Edit->setValue(product->est_abv);
    ui->est_abvShow->setValue(product->est_abv);

    /*
     * Calculate kilocalories/liter. Formula from brouwhulp.
     * Take the alcohol and sugar parts and then combine.
     */
    double alc = 1881.22 * product->est_fg * (product->est_og - product->est_fg) / (1.775 - product->est_og);
    double 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)));

    // Bottle priming
    double priming_total = 0;
    for (i = 0; i < product->fermentables.size(); i++) {
	if (product->fermentables.at(i).f_added == FERMENTABLE_ADDED_BOTTLE) {
	    priming_total += ((product->fermentables.at(i).f_yield / 100) * (1 - product->fermentables.at(i).f_moisture / 100)) *
		    		product->fermentables.at(i).f_amount;
	    qDebug() << "  priming" << product->fermentables.at(i).f_amount << "total" << priming_total;
	}
    }
    double grl = priming_total * 1000.0 * (1 / product->batch_size);
    double volco2 = grl * 0.510;
    qDebug() << "  priming gr/l" << grl << "volco2" << volco2;

    if (volco2 > 0) {
	product->est_carb = volco2;
	ui->est_carbEdit->setValue(product->est_carb);
	ui->est_carbShow->setValue(product->est_carb);
    }
}


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).f_added < FERMENTABLE_ADDED_BOTTLE) {
	    d = product->fermentables.at(i).f_percentage / 100.0 *
		    (product->fermentables.at(i).f_yield / 100.0) *
		    (1 - product->fermentables.at(i).f_moisture / 100.0);
	    if (product->fermentables.at(i).f_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).f_percentage * 10.0 * totmass) / 1000.0;
	    product->fermentables[i].f_amount = amount;
	}
    }
}


void EditProduct::ferment_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 EditProduct::ferment_perc_sugars_valueChanged(int value)
{
    if (value < 20)
	ui->perc_sugarsShow->setStyleSheet(bar_green);
    else
	ui->perc_sugarsShow->setStyleSheet(bar_red);
}


void EditProduct::ferment_perc_cara_valueChanged(int value)
{
    if (value < 25)
	ui->perc_caraShow->setStyleSheet(bar_green);
    else
	ui->perc_caraShow->setStyleSheet(bar_red);
}


void EditProduct::ferment_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 EditProduct::addFermentRow_clicked()
{
    Fermentables newf;

    qDebug() << "Add fermentable row";

    for (int i = 0; i < product->fermentables.size(); i++) {
	if (product->fermentables.at(i).f_amount == 0 && product->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 = FERMENTABLE_TYPE_GRAIN;
    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 = FERMENTABLE_GRAINTYPE_BASE;
    newf.f_added = FERMENTABLE_ADDED_MASH;
    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;

    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).f_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).f_added < FERMENTABLE_ADDED_BOTTLE)	// Only before bottle/kegging
            total += product->fermentables.at(i).f_amount;
    for (int i = 0; i < product->fermentables.size(); i++)
        if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE)
            product->fermentables[i].f_percentage = product->fermentables.at(i).f_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).f_added < FERMENTABLE_ADDED_BOTTLE)
	return;

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

    product->fermentables[product->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(product->fermentables_row, 7, item);

    for (int i = 0; i < product->fermentables.size(); i++)
	if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE)	// Only before bottle/kegging
	    total += product->fermentables.at(i).f_amount;
    /*
     * Recalculate the percentages
     */
    for (int i = 0; i < product->fermentables.size(); i++) {
	if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE) {
	    perc = product->fermentables.at(i).f_amount / total * 100;
	    product->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 == 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).f_added < FERMENTABLE_ADDED_BOTTLE)	// Only before bottle/kegging
            total += product->fermentables.at(i).f_amount;
	if (product->fermentables.at(i).f_adjust_to_total_100)
	    row100 = i;
    }
    double oldperc = product->fermentables.at(product->fermentables_row).f_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].f_percentage += diffp;
    product->fermentables[product->fermentables_row].f_amount += diffw;
    product->fermentables[row100].f_percentage -= diffp;
    product->fermentables[row100].f_amount -= diffw;

    item = new QTableWidgetItem(QString("%1 Kg").arg(product->fermentables[product->fermentables_row].f_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].f_amount);

    item = new QTableWidgetItem(QString("%1%").arg(product->fermentables[product->fermentables_row].f_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].f_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].f_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).f_added >= FERMENTABLE_ADDED_BOTTLE) {
	const QSignalBlocker blocker1(to100Edit);
	product->fermentables[product->fermentables_row].f_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].f_adjust_to_total_100 = true;
	pctEdit->setReadOnly(false);
	famountEdit->setReadOnly(true);
    } else if (product->fermentables_use100 && product->fermentables[product->fermentables_row].f_adjust_to_total_100 && ! val) {
	/* Scenario 2. */
	product->fermentables[product->fermentables_row].f_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].f_adjust_to_total_100 && val) {
	/* Scenario 3. */
	for (int i = 0; i < product->fermentables.size(); i++) {
	    product->fermentables[i].f_adjust_to_total_100 = false;
	}
	product->fermentables[product->fermentables_row].f_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].f_name = query.value(0).toString();
    product->fermentables[product->fermentables_row].f_origin = query.value(1).toString();
    product->fermentables[product->fermentables_row].f_supplier = query.value(2).toString();
    product->fermentables[product->fermentables_row].f_cost = query.value(3).toDouble();
    product->fermentables[product->fermentables_row].f_type = query.value(4).toInt();
    product->fermentables[product->fermentables_row].f_yield = query.value(5).toDouble();
    product->fermentables[product->fermentables_row].f_color = query.value(6).toDouble();
    product->fermentables[product->fermentables_row].f_coarse_fine_diff = query.value(7).toDouble();
    product->fermentables[product->fermentables_row].f_moisture = query.value(8).toDouble();
    product->fermentables[product->fermentables_row].f_diastatic_power = query.value(9).toDouble();
    product->fermentables[product->fermentables_row].f_protein = query.value(10).toDouble();
    product->fermentables[product->fermentables_row].f_dissolved_protein = query.value(11).toDouble();
    product->fermentables[product->fermentables_row].f_max_in_batch = query.value(12).toDouble();
    product->fermentables[product->fermentables_row].f_graintype = query.value(13).toInt();
    product->fermentables[product->fermentables_row].f_recommend_mash = query.value(14).toInt() ? true:false;
    product->fermentables[product->fermentables_row].f_add_after_boil = query.value(15).toInt() ? true:false;
    product->fermentables[product->fermentables_row].f_di_ph = query.value(16).toDouble();
    product->fermentables[product->fermentables_row].f_acid_to_ph_57 = query.value(17).toDouble();
    product->fermentables[product->fermentables_row].f_inventory = query.value(18).toDouble();

    /*
     * Update the visible fields
     */
    fnameEdit->setText(product->fermentables.at(product->fermentables_row).f_name);
    fsupplierEdit->setText(product->fermentables.at(product->fermentables_row).f_supplier);
    fmaxEdit->setValue(product->fermentables.at(product->fermentables_row).f_max_in_batch);

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

    w = QString("%1").arg(product->fermentables.at(product->fermentables_row).f_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(fermentable_types[product->fermentables.at(product->fermentables_row).f_type]);
    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
    ui->fermentablesTable->setItem(product->fermentables_row, 3, item);

    item = new QTableWidgetItem(fermentable_graintypes[product->fermentables.at(product->fermentables_row).f_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).f_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).f_inventory, 4, 'f', 3, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    if (product->fermentables.at(product->fermentables_row).f_inventory < product->fermentables.at(product->fermentables_row).f_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].f_added = val;
    QTableWidgetItem *item = new QTableWidgetItem(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).f_added < FERMENTABLE_ADDED_BOTTLE);
    pctEdit->setReadOnly(! (product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < FERMENTABLE_ADDED_BOTTLE));

    double total = 0;
    for (int i = 0; i < product->fermentables.size(); i++)
        if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE)	// Only before bottle/kegging
            total += product->fermentables.at(i).f_amount;
    for (int i = 0; i < product->fermentables.size(); i++)
        if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE)
            product->fermentables[i].f_percentage = product->fermentables.at(i).f_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).f_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).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(product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < 4);
    famountEdit->setMaximum(100000.0);
    famountEdit->setSingleStep(0.0010);
    famountEdit->setValue(product->fermentables.at(product->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 (product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < FERMENTABLE_ADDED_BOTTLE) {
    	if (product->fermentables.at(product->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(product->fermentables.at(product->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->setCurrentIndex(product->fermentables.at(product->fermentables_row).f_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).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(product->fermentables.at(product->fermentables_row).f_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).f_added < FERMENTABLE_ADDED_BOTTLE)	// Only before bottle/kegging
		total += product->fermentables.at(i).f_amount;
	product->fermentables_use100 = false;
	for (int i = 0; i < product->fermentables.size(); i++) {
	    if (product->fermentables.at(i).f_adjust_to_total_100)
	    	product->fermentables_use100 = true;
	    if (product->fermentables.at(i).f_added < FERMENTABLE_ADDED_BOTTLE) {
		product->fermentables[i].f_percentage = product->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();
}

mercurial