src/EditRecipeTab6.cpp

Mon, 06 Jun 2022 17:15:27 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Mon, 06 Jun 2022 17:15:27 +0200
changeset 260
42b88d85fefc
parent 171
6cd2d808d863
child 287
83e66c6b6e07
permissions
-rw-r--r--

Fix default divide_size field in products. Update miscs table column 6 and 7 tooltips and display of the buttons after sort. After a new misc product is selected, update the current row index because the row may be moved. Fix some display misc values in the checklist, they were not multiplied by 1000. Fix display of some bars if the value was 24.

/**
 * EditRecipe.cpp is part of bmsapp.
 *
 * tab 6, mash.
 *
 * bmsapp is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * bmsapp is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


void EditRecipe::refreshMashs()
{
    QString w;
    QWidget* pWidget;
    QHBoxLayout* pLayout;
    QTableWidgetItem *item;
    QIcon down_icon, up_icon;

    qDebug() << "refreshMashs" << recipe->mashs.size();

    down_icon.addFile(QString::fromUtf8(":/icons/silk/bullet_arrow_down.png"), QSize(), QIcon::Normal, QIcon::Off);
    up_icon.addFile(QString::fromUtf8(":/icons/silk/bullet_arrow_up.png"), QSize(), QIcon::Normal, QIcon::Off);

    const QStringList labels({tr("Step name"), tr("Type"), tr("Start"), tr("End"), tr("Rest"), tr("Ramp"),
			      tr("Inf/dec"), tr("Inf/dec"), tr("Volume"), tr("W/G ratio"), "", "", tr("Delete"), tr("Edit") });

    ui->mashsTable->setColumnCount(14);
    ui->mashsTable->setColumnWidth(0, 189);	/* Step name	*/
    ui->mashsTable->setColumnWidth(1, 100);	/* Type		*/
    ui->mashsTable->setColumnWidth(2,  70);	/* Start temp	*/
    ui->mashsTable->setColumnWidth(3,  70);	/* End temp	*/
    ui->mashsTable->setColumnWidth(4,  70);	/* Rest time	*/
    ui->mashsTable->setColumnWidth(5,  70);	/* Ramp time	*/
    ui->mashsTable->setColumnWidth(6,  70);	/* Infusion vol	*/
    ui->mashsTable->setColumnWidth(7,  70);     /* Infusion tmp	*/
    ui->mashsTable->setColumnWidth(8,  70);	/* Volume	*/
    ui->mashsTable->setColumnWidth(9,  80);	/* W/G ratio	*/
    ui->mashsTable->setColumnWidth(10, 30);	/* Up button	*/
    ui->mashsTable->setColumnWidth(11, 30);	/* Down button	*/
    ui->mashsTable->setColumnWidth(12, 80);	/* Delete	*/
    ui->mashsTable->setColumnWidth(13, 80);	/* Edit		*/
    ui->mashsTable->setHorizontalHeaderLabels(labels);
    ui->mashsTable->verticalHeader()->hide();
    ui->mashsTable->setRowCount(recipe->mashs.size());

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

	ui->mashsTable->setItem(i, 0, new QTableWidgetItem(recipe->mashs.at(i).step_name));

	item = new QTableWidgetItem(step_types[recipe->mashs.at(i).step_type]);
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        ui->mashsTable->setItem(i, 1, item);

	item = new QTableWidgetItem(QString("%1 °C").arg(recipe->mashs.at(i).step_temp, 2, 'f', 1, '0'));
	item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
	ui->mashsTable->setItem(i, 2, item);

	item = new QTableWidgetItem(QString("%1 °C").arg(recipe->mashs.at(i).end_temp, 2, 'f', 1, '0'));
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        ui->mashsTable->setItem(i, 3, item);

	item = new QTableWidgetItem(QString("%1 min").arg(recipe->mashs.at(i).step_time, 1, 'f', 0, '0'));
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        ui->mashsTable->setItem(i, 4, item);

	item = new QTableWidgetItem(QString("%1 min").arg(recipe->mashs.at(i).ramp_time, 1, 'f', 0, '0'));
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        ui->mashsTable->setItem(i, 5, item);

	if (recipe->mashs.at(i).step_infuse_amount > 0) {
	    item = new QTableWidgetItem(QString("%1 L").arg(recipe->mashs.at(i).step_infuse_amount, 2, 'f', 1, '0'));
            item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
            ui->mashsTable->setItem(i, 6, item);
	    item = new QTableWidgetItem(QString("%1 °C").arg(recipe->mashs.at(i).step_infuse_temp, 3, 'f', 2, '0'));
            item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
            ui->mashsTable->setItem(i, 7, item);
	} else {
	    ui->mashsTable->removeCellWidget(i, 6);
	    ui->mashsTable->removeCellWidget(i, 7);
	}

	item = new QTableWidgetItem(QString("%1 L").arg(recipe->mashs.at(i).step_volume, 2, 'f', 1, '0'));
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        ui->mashsTable->setItem(i, 8, item);

	item = new QTableWidgetItem(QString("%1 L/kg").arg(recipe->mashs.at(i).step_wg_ratio, 3, 'f', 2, '0'));
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        ui->mashsTable->setItem(i, 9, item);

	if (i > 0) {
	    pWidget = new QWidget();
            QPushButton* btn_up = new QPushButton();
            btn_up->setObjectName(QString("%1").arg(i));  /* Send row with the button */
            btn_up->setIcon(up_icon);
            connect(btn_up, SIGNAL(clicked()), this, SLOT(upMashRow_clicked()));
            pLayout = new QHBoxLayout(pWidget);
            pLayout->addWidget(btn_up);
            pLayout->setContentsMargins(5, 0, 5, 0);
            pWidget->setLayout(pLayout);
            ui->mashsTable->setCellWidget(i, 10, pWidget);
	} else {
	    ui->mashsTable->removeCellWidget(i, 10);
	}

	if (i < (recipe->mashs.size() - 1)) {
	    pWidget = new QWidget();
            QPushButton* btn_down = new QPushButton();
            btn_down->setObjectName(QString("%1").arg(i));  /* Send row with the button */
	    btn_down->setIcon(down_icon);
            connect(btn_down, SIGNAL(clicked()), this, SLOT(downMashRow_clicked()));
            pLayout = new QHBoxLayout(pWidget);
            pLayout->addWidget(btn_down);
            pLayout->setContentsMargins(5, 0, 5, 0);
            pWidget->setLayout(pLayout);
            ui->mashsTable->setCellWidget(i, 11, pWidget);
	} else {
	    ui->mashsTable->removeCellWidget(i, 11);
	}

	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(deleteMashRow_clicked()));
        pLayout = new QHBoxLayout(pWidget);
        pLayout->addWidget(btn_dele);
        pLayout->setContentsMargins(5, 0, 5, 0);
        pWidget->setLayout(pLayout);
        ui->mashsTable->setCellWidget(i, 12, 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(editMashRow_clicked()));
        pLayout = new QHBoxLayout(pWidget);
        pLayout->addWidget(btn_edit);
        pLayout->setContentsMargins(5, 0, 5, 0);
        pWidget->setLayout(pLayout);
        ui->mashsTable->setCellWidget(i, 13, pWidget);
    }
}


double EditRecipe::infusionVol(double step_infused, double step_mashkg, double infuse_temp, double step_temp, double last_temp)
{
    double a = last_temp * (equip_tun_weight * equip_tun_specific_heat + step_infused * SpecificHeatWater + step_mashkg * SpecificHeatMalt);
    double b = step_temp * (equip_tun_weight * equip_tun_specific_heat + step_infused * SpecificHeatWater + step_mashkg * SpecificHeatMalt);
    double vol = round(((b - a) / ((infuse_temp - step_temp) * SpecificHeatWater)) * 100.0) / 100.0;

    if (vol < 0)
	vol = 0;
    qDebug() << "  infusionVol(" << step_infused << "," << step_mashkg << "," << infuse_temp <<"," << step_temp << "," << last_temp << "):" << vol;
    return vol;
}


double EditRecipe::decoctionVol(double step_volume, double step_temp, double prev_temp)
{
    double a = (equip_tun_weight * equip_tun_specific_heat + step_volume * SpecificHeatWater) * (step_temp - prev_temp);
    double b = SpecificHeatWater * (99 - step_temp);
    double vol = 0;

    if (b > 0)
	vol = round((a / b) * 1000000.0) / 1000000.0;
    qDebug() << "  decoctionVol(" << step_volume << "," << step_temp << "," << prev_temp << "):" << vol;
    return vol;
}


void EditRecipe::calcMash()
{
    double infused = 0, vol, a, b, temp;
    int    i, j, n;
    double lasttemp = 18.0;
    double graintemp = 18.0;
    double tuntemp = 18.0;

    recipe->mashs_time = 0;

    if (recipe->mashs.size() && recipe->mashs_kg > 0) {
	qDebug() << "calcMash()";

	for (i = 0; i < recipe->mashs.size(); i++) {
	    if (recipe->mashs.at(i).step_type == 0) { // Infusion
		if (i == 0) {
		    // First mash step, temperature from the mashtun and malt.
		    n = 20; // tun is preheated.
		    tuntemp = recipe->mashs.at(i).step_temp;
		    for (j = 0; j < n; j++) {
			a = recipe->mashs_kg * graintemp * SpecificHeatMalt + equip_tun_weight * tuntemp * equip_tun_specific_heat;
			b = recipe->mashs[i].step_temp *
			      (equip_tun_weight * equip_tun_specific_heat +
			       recipe->mashs.at(i).step_infuse_amount * SpecificHeatWater + 
			       recipe->mashs_kg * SpecificHeatMalt) -
			      SlakingHeat * recipe->mashs_kg;
			if (recipe->mashs.at(i).step_infuse_amount > 0) {
			    temp = (b - a) / (recipe->mashs.at(i).step_infuse_amount * SpecificHeatWater);
			} else {
			    temp = 99;
			}
			tuntemp += (temp - tuntemp) / 2;
			recipe->mashs[i].step_infuse_temp = round(temp * 1000000.0) / 1000000.0;
		    }
		    qDebug() << "  init infuse temp:" << recipe->mashs.at(i).step_infuse_temp;
		} else {
		    // Calculate amount of infusion water.
		    recipe->mashs[i].step_infuse_amount =
			    infusionVol(infused, recipe->mashs_kg, recipe->mashs.at(i).step_infuse_temp, recipe->mashs.at(i).step_temp, lasttemp);
		    qDebug() << i << "  vol:" << recipe->mashs.at(i).step_infuse_amount << "temp:" << recipe->mashs.at(i).step_infuse_temp;
		}
		infused += recipe->mashs.at(i).step_infuse_amount;
	    } else if (recipe->mashs.at(i).step_type == 1) { // Temperature
		if (i > 0)
		    recipe->mashs[i].step_infuse_amount = 0;
		recipe->mashs[i].step_infuse_temp = 0;
	    } else if (recipe->mashs.at(i).step_type == 2) { // Decoction
		recipe->mashs[i].step_infuse_amount = decoctionVol(infused, recipe->mashs.at(i).step_temp, lasttemp);
		recipe->mashs[i].step_infuse_temp = 99;
	    }
	    recipe->mashs[i].step_volume = infused;
	    //qDebug() << i << "  type:" << recipe->mashs.at(i).step_type << "volume:" << recipe->mashs.at(i).step_infuse_amount << "temp:" << recipe->mashs.at(i).step_infuse_temp;
	    lasttemp = recipe->mashs.at(i).step_temp;
	    recipe->mashs_time += recipe->mashs.at(i).step_time;
	    if (i > 0)
	    	recipe->mashs_time += recipe->mashs.at(i).ramp_time;
	    recipe->mashs[i].step_wg_ratio = round((infused / recipe->mashs_kg) * 1000000.0) / 1000000.0;
	}
    }

    /* Show the calculated total mash time. */
    ui->mash_timeEdit->setText(QString("%1:%2").arg(recipe->mashs_time / 60).arg(recipe->mashs_time % 60, 2, 'f', 0, '0'));
}


void EditRecipe::addMashRow_clicked()
{
    Mashs newm;

    for (int i = 0; i < recipe->mashs.size(); i++) {
        if (recipe->mashs.at(i).step_time == 0)
            return;     // Add only one at a time.
    }

    newm.step_name = "Name me";
    newm.step_temp = newm.end_temp = 67.0;
    newm.step_time = 20;
    newm.ramp_time = 10;
    if (recipe->mashs.size()) {
	newm.step_volume = recipe->mashs.at(recipe->mashs.size() - 1).step_volume;
	newm.step_wg_ratio = recipe->mashs.at(recipe->mashs.size() - 1).step_wg_ratio;
	newm.step_type = recipe->mashs.at(recipe->mashs.size() - 1).step_type;
    } else {
	newm.step_volume = 0;
	newm.step_wg_ratio = 0;
	newm.step_type = 1;
    }
    newm.step_infuse_amount = newm.step_infuse_temp = 0;

    recipe->mashs.append(newm);
    is_changed();
    emit refreshAll();
}


void EditRecipe::deleteMashRow_clicked()
{
    if (recipe->locked || recipe->mashs.size() < 1)
	return;

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

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

    recipe->mashs.removeAt(row);
    is_changed();
    emit refreshAll();
}


void EditRecipe::upMashRow_clicked()
{
    if (recipe->locked || recipe->mashs.size() < 1)
        return;

    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
    int row = pb->objectName().toInt();
    qDebug() << "Move up mash row" << row << recipe->mashs.size();

    Mashs temp;
    temp = recipe->mashs[row - 1];
    recipe->mashs[row - 1] = recipe->mashs[row];
    recipe->mashs[row] = temp;
    is_changed();
    emit refreshAll();
}


void EditRecipe::downMashRow_clicked()
{
    if (recipe->locked || recipe->mashs.size() < 1)
        return;

    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
    int row = pb->objectName().toInt();
    qDebug() << "Move down mash row" << row << recipe->mashs.size();

    Mashs temp;
    temp = recipe->mashs[row + 1];
    recipe->mashs[row + 1] = recipe->mashs[row];
    recipe->mashs[row] = temp;
    is_changed();
    emit refreshAll();
}


void EditRecipe::step_name_changed(QString val)
{
    recipe->mashs[recipe->mashs_row].step_name = val;
    ui->mashsTable->setItem(recipe->mashs_row, 0, new QTableWidgetItem(val));
    is_changed();
}


void EditRecipe::step_type_changed(int val)
{
    qDebug() << "step_type_changed" << recipe->mashs_row << val;

    recipe->mashs[recipe->mashs_row].step_type = val;
    ivolLabel->setVisible(recipe->mashs.at(recipe->mashs_row).step_type == 0);
    stepivolEdit->setVisible(recipe->mashs.at(recipe->mashs_row).step_type == 0);
    itmpLabel->setVisible(recipe->mashs.at(recipe->mashs_row).step_type == 0);
    stepitmpEdit->setVisible(recipe->mashs.at(recipe->mashs_row).step_type == 0);
    is_changed();
    emit refreshAll();
}


void EditRecipe::step_temp_changed(double val)
{
    qDebug() << "step_temp_changed" << recipe->mashs_row << val;
    recipe->mashs[recipe->mashs_row].step_temp = val;
    QTableWidgetItem *item = new QTableWidgetItem(QString("%1 °C").arg(val, 2, 'f', 1, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->mashsTable->setItem(recipe->mashs_row, 2, item);
    is_changed();
    emit refreshAll();
}


void EditRecipe::end_temp_changed(double val)
{
    qDebug() << "end_temp_changed" << recipe->mashs_row << val;
    recipe->mashs[recipe->mashs_row].end_temp = val;
    QTableWidgetItem *item = new QTableWidgetItem(QString("%1 °C").arg(val, 2, 'f', 1, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->mashsTable->setItem(recipe->mashs_row, 3, item);
    is_changed();
    emit refreshAll();
}


void EditRecipe::step_time_changed(double val)
{
    qDebug() << "step_time_changed" << recipe->mashs_row << val;
    recipe->mashs[recipe->mashs_row].step_time = val;
    QTableWidgetItem *item = new QTableWidgetItem(QString("%1 min").arg(val, 1, 'f', 0, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->mashsTable->setItem(recipe->mashs_row, 4, item);
    is_changed();
    emit refreshAll();
}


void EditRecipe::ramp_time_changed(double val)
{
    qDebug() << "ramp_time_changed" << recipe->mashs_row << val;
    recipe->mashs[recipe->mashs_row].ramp_time = val;
    QTableWidgetItem *item = new QTableWidgetItem(QString("%1 min").arg(val, 1, 'f', 0, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->mashsTable->setItem(recipe->mashs_row, 5, item);
    is_changed();
    emit refreshAll();
}


void EditRecipe::infuse_changed(double val)
{
    qDebug() << "infuse_changed" << recipe->mashs_row << val;
    recipe->mashs[recipe->mashs_row].step_infuse_amount = val;
    QTableWidgetItem *item = new QTableWidgetItem(QString("%1 L").arg(val, 1, 'f', 0, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->mashsTable->setItem(recipe->mashs_row, 6, item);

    /*
     * Recalculate water volumes
     */
    double volume = 0;
    for (int i = 0; i < recipe->mashs.size(); i++) {
	if (recipe->mashs.at(i).step_type == 0) {
	    volume += recipe->mashs.at(i).step_infuse_amount;
	}
	recipe->mashs[i].step_volume = volume;
    }
    recipe->w1_amount = volume - recipe->w2_amount;
    recipe->wg_amount = volume;
    ui->w1_volEdit->setValue(recipe->w1_amount);

    is_changed();
    emit refreshAll();
}


void EditRecipe::editMashRow_clicked()
{
    QSqlQuery query;

    if (recipe->locked)
	return;

    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
    recipe->mashs_row = pb->objectName().toInt();
    qDebug() << "Edit mash row" << recipe->mashs_row;
    Mashs backup = recipe->mashs.at(recipe->mashs_row);

    QDialog* dialog = new QDialog(this);
    dialog->resize(738, 230);
    QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
    buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
    buttonBox->setGeometry(QRect(30, 180, 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("Step name:"));
    nameLabel->setGeometry(QRect(10, 10, 141, 20));
    nameLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    stepnameEdit = new QLineEdit(dialog);
    stepnameEdit->setObjectName(QString::fromUtf8("stepnameEdit"));
    stepnameEdit->setText(recipe->mashs.at(recipe->mashs_row).step_name);
    stepnameEdit->setGeometry(QRect(160, 10, 511, 23));

    QLabel *typeLabel = new QLabel(dialog);
    typeLabel->setObjectName(QString::fromUtf8("typeLabel"));
    typeLabel->setText(tr("Step type:"));
    typeLabel->setGeometry(QRect(10, 40, 141, 20));
    typeLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QComboBox *typeEdit = new QComboBox(dialog);
    typeEdit->setObjectName(QString::fromUtf8("typeEdit"));
    typeEdit->setGeometry(QRect(160, 40, 161, 23));
    typeEdit->addItem(tr("Infusion"));
    typeEdit->addItem(tr("Temperature"));
    typeEdit->addItem(tr("Decoction"));
    typeEdit->setCurrentIndex(recipe->mashs.at(recipe->mashs_row).step_type);

    QLabel *tempLabel = new QLabel(dialog);
    tempLabel->setObjectName(QString::fromUtf8("tempLabel"));
    tempLabel->setText(tr("Step start temp:"));
    tempLabel->setGeometry(QRect(10, 70, 141, 20));
    tempLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    steptempEdit = new QDoubleSpinBox(dialog);
    steptempEdit->setObjectName(QString::fromUtf8("steptempEdit"));
    steptempEdit->setGeometry(QRect(160, 70, 121, 24));
    steptempEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    steptempEdit->setDecimals(1);
    steptempEdit->setValue(recipe->mashs.at(recipe->mashs_row).step_temp);

    QLabel *endLabel = new QLabel(dialog);
    endLabel->setObjectName(QString::fromUtf8("endLabel"));
    endLabel->setText(tr("Step end temp:"));
    endLabel->setGeometry(QRect(360, 70, 141, 20));
    endLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    endtempEdit = new QDoubleSpinBox(dialog);
    endtempEdit->setObjectName(QString::fromUtf8("endtempEdit"));
    endtempEdit->setGeometry(QRect(510, 70, 121, 24));
    endtempEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    endtempEdit->setDecimals(1);
    endtempEdit->setValue(recipe->mashs.at(recipe->mashs_row).end_temp);

    QLabel *timeLabel = new QLabel(dialog);
    timeLabel->setObjectName(QString::fromUtf8("timeLabel"));
    timeLabel->setText(tr("Step rest time:"));
    timeLabel->setGeometry(QRect(10, 100, 141, 20));
    timeLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    steptimeEdit = new QDoubleSpinBox(dialog);
    steptimeEdit->setObjectName(QString::fromUtf8("steptimeEdit"));
    steptimeEdit->setGeometry(QRect(160, 100, 121, 24));
    steptimeEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    steptimeEdit->setDecimals(0);
    steptimeEdit->setValue(recipe->mashs.at(recipe->mashs_row).step_time);

    QLabel *rampLabel = new QLabel(dialog);
    rampLabel->setObjectName(QString::fromUtf8("rampLabel"));
    rampLabel->setText(tr("Step ramp time:"));
    rampLabel->setGeometry(QRect(360, 100, 141, 20));
    rampLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    ramptimeEdit = new QDoubleSpinBox(dialog);
    ramptimeEdit->setObjectName(QString::fromUtf8("ramptimeEdit"));
    ramptimeEdit->setGeometry(QRect(510, 100, 121, 24));
    ramptimeEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    ramptimeEdit->setDecimals(0);
    ramptimeEdit->setValue(recipe->mashs.at(recipe->mashs_row).ramp_time);

    /*
     * Only used for Infusion steps.
     */
    ivolLabel = new QLabel(dialog);
    ivolLabel->setObjectName(QString::fromUtf8("ivolLabel"));
    ivolLabel->setText(tr("Infusion volume:"));
    ivolLabel->setGeometry(QRect(10, 130, 141, 20));
    ivolLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    ivolLabel->setVisible(recipe->mashs.at(recipe->mashs_row).step_type == 0);
    stepivolEdit = new QDoubleSpinBox(dialog);
    stepivolEdit->setObjectName(QString::fromUtf8("stepivolEdit"));
    stepivolEdit->setGeometry(QRect(160, 130, 121, 24));
    stepivolEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    stepivolEdit->setVisible(recipe->mashs.at(recipe->mashs_row).step_type == 0);
    stepivolEdit->setDecimals(1);
    stepivolEdit->setAccelerated(true);
    stepivolEdit->setMaximum(100000.0);
    stepivolEdit->setSingleStep(0.5);
    stepivolEdit->setValue(recipe->mashs.at(recipe->mashs_row).step_infuse_amount);

    itmpLabel = new QLabel(dialog);
    itmpLabel->setObjectName(QString::fromUtf8("itmpLabel"));
    itmpLabel->setText(tr("Infusion Temperature:"));
    itmpLabel->setGeometry(QRect(360, 130, 141, 20));
    itmpLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    itmpLabel->setVisible(recipe->mashs.at(recipe->mashs_row).step_type == 0);
    stepitmpEdit = new QDoubleSpinBox(dialog);
    stepitmpEdit->setObjectName(QString::fromUtf8("stepitmpEdit"));
    stepitmpEdit->setGeometry(QRect(510, 130, 121, 24));
    stepitmpEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    stepitmpEdit->setVisible(recipe->mashs.at(recipe->mashs_row).step_type == 0);
    stepitmpEdit->setDecimals(1);
    stepitmpEdit->setReadOnly(true);
    stepitmpEdit->setButtonSymbols(QAbstractSpinBox::NoButtons);
    stepitmpEdit->setValue(recipe->mashs.at(recipe->mashs_row).step_infuse_temp);

    connect(stepnameEdit, &QLineEdit::textEdited, this, &EditRecipe::step_name_changed);
    connect(typeEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditRecipe::step_type_changed);
    connect(steptempEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditRecipe::step_temp_changed);
    connect(endtempEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditRecipe::end_temp_changed);
    connect(steptimeEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditRecipe::step_time_changed);
    connect(ramptimeEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditRecipe::ramp_time_changed);
    connect(stepivolEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditRecipe::infuse_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->mashs[recipe->mashs_row] = backup;
	/* Rollback water volumes too */
	double volume = 0;
    	for (int i = 0; i < recipe->mashs.size(); i++) {
            if (recipe->mashs.at(i).step_type == 0) {
            	volume += recipe->mashs.at(i).step_infuse_amount;
            }
            recipe->mashs[i].step_volume = volume;
    	}
    	recipe->w1_amount = volume - recipe->w2_amount;
    	recipe->wg_amount = volume;
    	ui->w1_volEdit->setValue(recipe->w1_amount);
    }

    disconnect(stepnameEdit, nullptr, nullptr, nullptr);
    disconnect(steptempEdit, nullptr, nullptr, nullptr);
    disconnect(endtempEdit, nullptr, nullptr, nullptr);
    disconnect(steptimeEdit, nullptr, nullptr, nullptr);
    disconnect(ramptimeEdit, nullptr, nullptr, nullptr);
    disconnect(stepivolEdit, nullptr, nullptr, nullptr);
    disconnect(buttonBox, nullptr, nullptr, nullptr);

    emit refreshAll();
}


void EditRecipe::mash_name_changed(QString name)
{
    qDebug() << "mash_name_changed" << name;
    recipe->mash_name = name;
    is_changed();
}


void EditRecipe::mash_select_changed(int val)
{
    QSqlQuery query;
    int  i;

    qDebug() << "mash_select_changed" << val;

    const QSignalBlocker blocker1(ui->mash_nameEdit);
    query.prepare("SELECT name,steps FROM profile_mash ORDER BY name");
    query.exec();
    query.first();
    for (i = 0; i < (val -1); i++) {
	query.next();
    }

    recipe->mash_name = query.value(0).toString();
    ui->mash_nameEdit->setText(recipe->mash_name);

    QJsonParseError parseError;
    const auto& json = query.value(1).toString();
    if (!json.trimmed().isEmpty()) {
	const auto& formattedJson = QString("%1").arg(json);
	QJsonDocument newsteps = QJsonDocument::fromJson(formattedJson.toUtf8(), &parseError);

	if (parseError.error != QJsonParseError::NoError) {
	    qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset;
	} else {
	    /*
	     * Got the json data in the steps array, replace the recipe steps.
	     */
	    double infuse = 0;
	    if (recipe->mashs.size()) {
	    	infuse = recipe->mashs.at(0).step_infuse_amount;
	    	recipe->mashs.clear();
	    	ui->mashsTable->clear();
	    }
	    if (newsteps.isArray()) {
		for (i = 0; i < newsteps.array().size(); i++) {
		    QJsonObject obj = newsteps.array().at(i).toObject();
		    Mashs m;
		    m.step_name = obj["step_name"].toString();
		    if (obj["step_type"].isString())
			m.step_type = QString(obj["step_type"].toString()).toInt();
		    else
		        m.step_type = obj["step_type"].toInt();
		    m.step_volume = 0;
		    m.step_infuse_amount = 0;
		    m.step_infuse_temp = 0;
		    if (obj["step_temp"].isString())
			m.step_temp = QString(obj["step_temp"].toString()).toDouble();
		    else
		        m.step_temp = obj["step_temp"].toDouble();
		    if (obj["step_time"].isString())
			m.step_time = QString(obj["step_time"].toString()).toDouble();
		    else
		        m.step_time = obj["step_time"].toDouble();
		    if (obj["ramp_time"].isString())
			m.ramp_time = QString(obj["ramp_time"].toString()).toDouble();
		    else
		        m.ramp_time = obj["ramp_time"].toDouble();
		    if (obj["end_temp"].isString())
			m.end_temp = QString(obj["end_temp"].toString()).toDouble();
		    else
		        m.end_temp = obj["end_temp"].toDouble();
		    m.step_wg_ratio = 0;
		    recipe->mashs.append(m);
		}
	    }
	    if (recipe->mashs.at(0).step_type == 0)
		recipe->mashs[0].step_infuse_amount = infuse;	// Restore saved initial infusion
	}
    }
    is_changed();
    emit refreshAll();
}

mercurial