src/EditRecipeTab2.cpp

Thu, 18 Aug 2022 20:34:15 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Thu, 18 Aug 2022 20:34:15 +0200
changeset 401
583148eb6e01
parent 369
09d3d8d18f97
child 449
00757c056ca6
permissions
-rw-r--r--

Init est_carb field for new products.

/**
 * EditRecipe.cpp is part of bmsapp.
 *
 * Tab 2, 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 EditRecipe::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 EditRecipe::to100Fermentables(int row)
{
    if (recipe->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, 9, pWidget);
    } else {
	ui->fermentablesTable->removeCellWidget(row, 9);
    }
}


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

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

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

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

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

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

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

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

	to100Fermentables(i);

	/* Add the Delete row button */
        pWidget = new QWidget();
        QPushButton* btn_dele = new QPushButton();
        btn_dele->setObjectName(QString("%1").arg(i));  /* Send row with the button */
        btn_dele->setText(tr("Delete"));
        connect(btn_dele, SIGNAL(clicked()), this, SLOT(deleteFermentRow_clicked()));
        pLayout = new QHBoxLayout(pWidget);
        pLayout->addWidget(btn_dele);
        pLayout->setContentsMargins(5, 0, 5, 0);
        pWidget->setLayout(pLayout);
        ui->fermentablesTable->setCellWidget(i, 10, pWidget);

	pWidget = new QWidget();
	QPushButton* btn_edit = new QPushButton();
	btn_edit->setObjectName(QString("%1").arg(i));  /* Send row with the button */
	btn_edit->setText(tr("Edit"));
	connect(btn_edit, SIGNAL(clicked()), this, SLOT(editFermentRow_clicked()));
	pLayout = new QHBoxLayout(pWidget);
        pLayout->addWidget(btn_edit);
	pLayout->setContentsMargins(5, 0, 5, 0);
        pWidget->setLayout(pLayout);
        ui->fermentablesTable->setCellWidget(i, 11, pWidget);
    }
}


void EditRecipe::calcFermentables()
{
    int		i;
    double	psugar = 0, pcara = 0, d, s = 0, x, color;
    double	vol = 0;		// Volume sugars after boil
    double	addedS = 0;		// Added sugars after boil
    double	addedmass = 0;		// Added mass after boil
    double	mvol = 0;		// Mash volume
    double	lintner = 0;		// Total recipe lintner
    double	sugarsf = 0;		// fermentable sugars mash + boil
    double	sugarsm = 0;		// fermentable sugars in mash
    double	sugardensity = 1.611;	// kg/l in solution
    double	mashtime = 0;		// Total mash time
    double	mashtemp = 0;		// Average mash temperature
    double	mashinfuse = 0;		// Mash infuse amount
    double	colort = 0;		// Colors srm * vol totals
    double	colorh = 0;		// Colors ebc * vol * kt
    double	colorn = 0;		// Colors ebc * pt * pct

    qDebug() << "calcFermentables()";

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

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

    if (recipe->fermentables.size() < 1) {
	qDebug() << "  no fermentables, return.";
	recipe->est_og = 0.980;
    	ui->est_ogEdit->setValue(0.980);
    	ui->est_og2Edit->setValue(0.980);
    	ui->est_og3Edit->setValue(0.980);
    	ui->est_ogShow->setValue(0.980);
    	recipe->est_color = 0;
    	ui->est_colorEdit->setValue(0);
    	ui->est_colorEdit->setStyleSheet(Utils::ebc_to_style(0));
    	ui->est_color2Edit->setValue(0);
    	ui->est_color2Edit->setStyleSheet(Utils::ebc_to_style(0));
    	ui->est_colorShow->setValue(0);
	ui->perc_mashShow->setValue(0);
	ui->perc_sugarsShow->setValue(0);
	ui->perc_caraShow->setValue(0);
	ui->lintnerShow->setValue(0);
    	recipe->est_fg = 0.980;
    	ui->est_fgEdit->setValue(0.980);
    	ui->est_fg3Edit->setValue(0.980);
    	ui->est_fgShow->setValue(0.980);
    	ui->est_abvEdit->setValue(0);
    	ui->est_abv2Edit->setValue(0);
    	ui->est_abvShow->setValue(0);
    	recipe->est_abv = 0;
    	ui->calEdit->setValue(0);
	recipe->mashs_kg = 0;
	return;
    }
    qDebug() << "  adjust to 100" << recipe->fermentables_use100;

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

    double og = Utils::estimate_sg(sugarsf + addedS, recipe->batch_size);
    qDebug() << "  OG" << ui->est_ogEdit->value() << og;
    recipe->est_og = og;
    ui->est_ogEdit->setValue(og);
    ui->est_og2Edit->setValue(og);
    ui->est_og3Edit->setValue(og);
    ui->est_ogShow->setValue(og);

    recipe->preboil_sg = Utils::estimate_sg(sugarsm, recipe->boil_size);
    qDebug() << "  preboil SG" << recipe->preboil_sg;

    /*
     * Color of the wort
     */
    if (recipe->color_method == 4) {				// Naudts
	color = round(((Utils::sg_to_plato(og) / 8.6) * colorn) + (recipe->boil_time / 60));
    } else if (recipe->color_method == 3) {			// Hans Halberstadt
	double bv = 0.925;					// Beer loss efficiency
	double sr = 0.95;					// Mash and sparge efficiency
	color = round((4.46 * bv * sr) / recipe->batch_size * colorh);
    } else {
	double cw = colort / recipe->batch_size * 8.34436;
	color = Utils::kw_to_ebc(recipe->color_method, cw);
	//qDebug() << "  oud EBC" << color << "new EBC" << Utils::kw_to_newebc(recipe->color_method, cw) << "SRM" << Utils::kw_to_srm(recipe->color_method, cw);
    }
    qDebug() << "  color" << ui->est_colorEdit->value() << color << recipe->est_color;
    recipe->est_color = color;
    ui->est_colorEdit->setValue(color);
    ui->est_colorEdit->setStyleSheet(Utils::ebc_to_style(color));
    ui->est_color2Edit->setValue(color);
    ui->est_color2Edit->setStyleSheet(Utils::ebc_to_style(color));
    ui->est_colorShow->setValue(color);

    /*
     * We don't have a equipment profile in recipes,
     * so we assume a certain guessed mashtun size.
     */
    ui->perc_mashShow->setValue(round(recipe->mashs_kg / (recipe->boil_size / 3) * 100));
    ui->perc_sugarsShow->setValue(round(psugar));
    ui->perc_caraShow->setValue(round(pcara));
    if (recipe->mashs_kg > 0) {
	qDebug() << "  lintner" << lintner << "  mashkg" << recipe->mashs_kg << "final" << round(lintner / recipe->mashs_kg);
	ui->lintnerShow->setValue(round(lintner / recipe->mashs_kg));
    } else {
	qDebug() << "  lintner N/A";
	ui->lintnerShow->setValue(0);
    }

    /*
     * Calculate the apparant attenuation.
     */
    double svg = 0;
    if (recipe->yeasts.size() > 0) {
        for (i = 0; i < recipe->yeasts.size(); i++) {
	    if (recipe->yeasts.at(i).use == 0) {		// Used in primary
		if (recipe->yeasts.at(i).attenuation > svg)
		    svg = recipe->yeasts.at(i).attenuation;	// Take the highest if multiple yeasts.
	    }
	    // TODO: brett or others in secondary.
	}
	qDebug() << "  SVG" << svg;
    }
    if (svg == 0)
	svg = 77.0;
    ui->est_svgEdit->setValue(svg);

    double fg;
    if (recipe->mashs_kg > 0 && mashinfuse > 0 && mashtime > 0 && mashtemp > 0)
	fg = Utils::estimate_fg(psugar, pcara, mashinfuse / recipe->mashs_kg, mashtime, mashtemp, svg, og);
    else
	fg = Utils::estimate_fg(psugar, pcara, 0, 0, 0, svg, og);
    qDebug() << "  FG" << ui->est_fgEdit->value() << fg;
    recipe->est_fg = fg;
    ui->est_fgEdit->setValue(fg);
    ui->est_fg3Edit->setValue(fg);
    ui->est_fgShow->setValue(fg);

    double abv = Utils::abvol(og, fg);
    qDebug() << "  ABV" << ui->est_abvEdit->value() << abv;
    ui->est_abvEdit->setValue(abv);
    ui->est_abv2Edit->setValue(abv);
    ui->est_abvShow->setValue(abv);
    recipe->est_abv = abv;

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

    // Bottle priming
    double priming_total = 0;
    for (i = 0; i < recipe->fermentables.size(); i++) {
	if (recipe->fermentables.at(i).added == 4) {
	    priming_total += ((recipe->fermentables.at(i).yield / 100) * (1 - recipe->fermentables.at(i).moisture / 100)) *
		    		recipe->fermentables.at(i).amount;
	    qDebug() << "  priming" << recipe->fermentables.at(i).amount << "total" << priming_total;
	}
    }
    double grl = priming_total * 1000.0 * (1 / recipe->batch_size);
    double volco2 = grl * 0.510;
    qDebug() << "  priming gr/l" << grl << "volco2" << volco2;

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


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

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

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

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


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

    qDebug() << "Add fermentable row";

    for (int i = 0; i < recipe->fermentables.size(); i++) {
	if (recipe->fermentables.at(i).amount == 0 && recipe->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 = 0;
    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 = 0;
    newf.added = 0;
    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;

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


void EditRecipe::deleteFermentRow_clicked()
{
    if (recipe->locked)
	return;

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

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

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

    recipe->fermentables.removeAt(row);

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

    is_changed();
    emit refreshAll();
}


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

    if (recipe->fermentables_use100 && recipe->fermentables.at(recipe->fermentables_row).added < 4)
	return;

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

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

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


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

    if (! recipe->fermentables_use100)
        return;

    qDebug() << "ferment_pct_changed()" << recipe->fermentables_row << val;
    /*
     * Since we have arrived here, adjust_to_100 is active and
     * this is not the entry to be adjusted to 100.
     */
    for (int i = 0; i < recipe->fermentables.size(); i++) {
        if (recipe->fermentables.at(i).added < 4)             // Only before bottle/kegging
            total += recipe->fermentables.at(i).amount;
	if (recipe->fermentables.at(i).adjust_to_total_100)
	    row100 = i;
    }
    double oldperc = recipe->fermentables.at(recipe->fermentables_row).percentage;
    double diffp = val - oldperc;
    double diffw = (diffp / 100) * total;
    qDebug() << "row100" << row100 << "total" << total << "diff kg" << diffw << "diff %" << diffp;

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

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

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

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

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

    is_changed();
}


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

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

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


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

    if (val < 1)
	return;

    qDebug() << "ferment_select_changed()" << recipe->fermentables_row << val << instock;

    /*
     * Search the fermentable pointed by the index and instock flag.
     */
    QString sql = "SELECT name,origin,supplier,cost,type,yield,color,coarse_fine_diff,moisture,diastatic_power,protein,dissolved_protein,max_in_batch,"
	  	  "graintype,recommend_mash,add_after_boil,di_ph,acid_to_ph_57 FROM inventory_fermentables ";
    if (instock)
	sql.append("WHERE inventory > 0 ");
    sql.append("ORDER BY supplier,name");
    query.prepare(sql);
    query.exec();
    query.first();
    for (int i = 0; i < (val - 1); i++) {
	query.next();
    }
    qDebug() << "found" << query.value(2).toString() << query.value(0).toString();

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

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

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

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

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

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

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

    calcFermentables();
    is_changed();
}


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

    qDebug() << "ferment_instock_changed()" << recipe->fermentables_row << val;

    this->fselectEdit->setCurrentIndex(-1);
    this->fselectEdit->clear();
    QString sql = "SELECT supplier,name,color,inventory FROM inventory_fermentables ";
    if (val)
	sql.append("WHERE inventory > 0 ");
    sql.append("ORDER BY supplier,name");
    query.prepare(sql);
    query.exec();
    query.first();
    this->fselectEdit->addItem("");	// Start with empty value
    for (int i = 0; i < query.size(); i++) {
        this->fselectEdit->addItem(query.value(0).toString()+" - "+query.value(1).toString()+" ("+query.value(2).toString()+" EBC) "+
			QString("%1 kg").arg(query.value(3).toDouble(), 4, 'f', 3, '0'));
        query.next();
    }
}


void EditRecipe::ferment_added_changed(int val)
{
    qDebug() << "ferment_added_changed()" << recipe->fermentables_row << val;

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

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

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

    is_changed();
    emit refreshAll();
}


void EditRecipe::editFermentRow_clicked()
{
    QSqlQuery query;

    if (recipe->locked)
	return;

    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
    recipe->fermentables_row = pb->objectName().toInt();
    qDebug() << "Edit fermentable row" << recipe->fermentables_row;
    Fermentables backup = recipe->fermentables.at(recipe->fermentables_row);

    QDialog* dialog = new QDialog(this);
    dialog->resize(738, 287);
    QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
    buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
    buttonBox->setGeometry(QRect(30, 240, 671, 32));
    buttonBox->setLayoutDirection(Qt::LeftToRight);
    buttonBox->setOrientation(Qt::Horizontal);
    buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
    buttonBox->setCenterButtons(true);
    QLabel *nameLabel = new QLabel(dialog);
    nameLabel->setObjectName(QString::fromUtf8("nameLabel"));
    nameLabel->setText(tr("Current ingredient:"));
    nameLabel->setGeometry(QRect(10, 10, 141, 20));
    nameLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *supplierLabel = new QLabel(dialog);
    supplierLabel->setObjectName(QString::fromUtf8("supplierLabel"));
    supplierLabel->setText(tr("Supplier:"));
    supplierLabel->setGeometry(QRect(10, 40, 141, 20));
    supplierLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *amountLabel = new QLabel(dialog);
    amountLabel->setObjectName(QString::fromUtf8("amountLabel"));
    amountLabel->setText(tr("Amount in kg:"));
    amountLabel->setGeometry(QRect(10, 100, 141, 20));
    amountLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *pctLabel = new QLabel(dialog);
    pctLabel->setObjectName(QString::fromUtf8("pctLabel"));
    pctLabel->setText(tr("Percentage in batch:"));
    pctLabel->setGeometry(QRect(10, 130, 141, 20));
    pctLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *to100Label = new QLabel(dialog);
    to100Label->setObjectName(QString::fromUtf8("to100Label"));
    to100Label->setText(tr("Auto fill to 100%:"));
    to100Label->setGeometry(QRect(10, 160, 141, 20));
    to100Label->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *addedLabel = new QLabel(dialog);
    addedLabel->setObjectName(QString::fromUtf8("addedLabel"));
    addedLabel->setText(tr("Use at:"));
    addedLabel->setGeometry(QRect(10, 190, 141, 20));
    addedLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *selectLabel = new QLabel(dialog);
    selectLabel->setObjectName(QString::fromUtf8("selectLabel"));
    selectLabel->setText(tr("Select ingredient:"));
    selectLabel->setGeometry(QRect(10, 70, 141, 20));
    selectLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *instockLabel = new QLabel(dialog);
    instockLabel->setObjectName(QString::fromUtf8("instockLabel"));
    instockLabel->setText(tr("In stock:"));
    instockLabel->setGeometry(QRect(525, 70, 121, 20));
    instockLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *maxLabel = new QLabel(dialog);
    maxLabel->setObjectName(QString::fromUtf8("maxLabel"));
    maxLabel->setText(tr("Max in batch:"));
    maxLabel->setGeometry(QRect(420, 130, 121, 20));
    maxLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);

    fselectEdit = new QComboBox(dialog);
    fselectEdit->setObjectName(QString::fromUtf8("fselectEdit"));
    fselectEdit->setGeometry(QRect(160, 70, 371, 23));

    fnameEdit = new QLineEdit(dialog);
    fnameEdit->setObjectName(QString::fromUtf8("fnameEdit"));
    fnameEdit->setText(recipe->fermentables.at(recipe->fermentables_row).name);
    fnameEdit->setGeometry(QRect(160, 10, 511, 23));
    fnameEdit->setReadOnly(true);
    fsupplierEdit = new QLineEdit(dialog);
    fsupplierEdit->setObjectName(QString::fromUtf8("fsupplierEdit"));
    fsupplierEdit->setText(recipe->fermentables.at(recipe->fermentables_row).supplier);
    fsupplierEdit->setGeometry(QRect(160, 40, 511, 23));
    fsupplierEdit->setReadOnly(true);
    famountEdit = new QDoubleSpinBox(dialog);
    famountEdit->setObjectName(QString::fromUtf8("famountEdit"));
    famountEdit->setGeometry(QRect(160, 100, 121, 24));
    famountEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    famountEdit->setAccelerated(true);
    famountEdit->setDecimals(3);
    famountEdit->setReadOnly(recipe->fermentables_use100 && recipe->fermentables.at(recipe->fermentables_row).added < 4);
    famountEdit->setMaximum(100000.0);
    famountEdit->setSingleStep(0.0010);
    famountEdit->setValue(recipe->fermentables.at(recipe->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 (recipe->fermentables_use100 && recipe->fermentables.at(recipe->fermentables_row).added < 4) {
    	if (recipe->fermentables.at(recipe->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(recipe->fermentables.at(recipe->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->addItem(tr("Bottle"));
    faddedEdit->addItem(tr("Kegs"));
    faddedEdit->setCurrentIndex(recipe->fermentables.at(recipe->fermentables_row).added);

    to100Edit = new QCheckBox(dialog);
    to100Edit->setObjectName(QString::fromUtf8("to100Edit"));
    to100Edit->setGeometry(QRect(160, 160, 85, 21));
    to100Edit->setChecked(recipe->fermentables.at(recipe->fermentables_row).adjust_to_total_100);

    finstockEdit = new QCheckBox(dialog);
    finstockEdit->setObjectName(QString::fromUtf8("instockEdit"));
    finstockEdit->setGeometry(QRect(655, 70, 85, 21));
    finstockEdit->setChecked(true);

    fmaxEdit = new QDoubleSpinBox(dialog);
    fmaxEdit->setObjectName(QString::fromUtf8("fmaxEdit"));
    fmaxEdit->setGeometry(QRect(550, 130, 121, 24));
    fmaxEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    fmaxEdit->setReadOnly(true);
    fmaxEdit->setButtonSymbols(QAbstractSpinBox::NoButtons);
    fmaxEdit->setDecimals(1);
    fmaxEdit->setValue(recipe->fermentables.at(recipe->fermentables_row).max_in_batch);

    ferment_instock_changed(true);

    connect(fselectEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditRecipe::ferment_select_changed);
    connect(famountEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditRecipe::ferment_amount_changed);
    connect(pctEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditRecipe::ferment_pct_changed);
    connect(faddedEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditRecipe::ferment_added_changed);
    connect(to100Edit, &QCheckBox::stateChanged, this, &EditRecipe::ferment_to100_changed);
    connect(finstockEdit, &QCheckBox::stateChanged, this, &EditRecipe::ferment_instock_changed);
    connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));
    connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));

    dialog->setModal(true);
    dialog->exec();
    if (dialog->result() == QDialog::Rejected) {
	qDebug() << "reject and rollback";
	recipe->fermentables[recipe->fermentables_row] = backup;
	/*
	 * Recalculate the percentages
	 */
	double total = 0;
	for (int i = 0; i < recipe->fermentables.size(); i++)
	    if (recipe->fermentables.at(i).added < 4)             // Only before bottle/kegging
		total += recipe->fermentables.at(i).amount;
	recipe->fermentables_use100 = false;
	for (int i = 0; i < recipe->fermentables.size(); i++) {
	    if (recipe->fermentables.at(i).adjust_to_total_100)
	    	recipe->fermentables_use100 = true;
	    if (recipe->fermentables.at(i).added < 4) {
		recipe->fermentables[i].percentage = recipe->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