src/EditProductTab4.cpp

Sun, 15 May 2022 11:27:06 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sun, 15 May 2022 11:27:06 +0200
changeset 208
615afedbcd25
parent 191
7446ee2fb427
child 215
4e2de71142a4
permissions
-rw-r--r--

Fixed 0 IBU for First Wort Hop or Mashhop is used.

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



bool EditProduct::hop_sort_test(const Hops &D1, const Hops &D2)
{
    if (D1.h_useat > D2.h_useat)
	return false;
    if (D1.h_useat < D2.h_useat)
	return true;
    /* Same useat moments, test time. */
    if (D1.h_time < D2.h_time)
	return false;
    if (D1.h_time > D2.h_time)
	return true;
    /* Finally consider the amounts */
    return (D1.h_amount > D2.h_amount);
}


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

    qDebug() << "refreshHops" << product->hops.size();
    std::sort(product->hops.begin(), product->hops.end(), hop_sort_test);

    const QStringList labels({tr("Origin"), tr("Hop"), tr("Type"), tr("Form"), tr("Alpha"), tr("Use at"), tr("Time"),
                              tr("IBU"), tr("Amount"), tr("Stock"), tr("Delete"), tr("Edit") });

    ui->hopsTable->setColumnCount(12);
    ui->hopsTable->setColumnWidth(0, 140);     /* Origin	*/
    ui->hopsTable->setColumnWidth(1, 225);     /* Hop		*/
    ui->hopsTable->setColumnWidth(2,  74);     /* Type		*/
    ui->hopsTable->setColumnWidth(3,  84);     /* Form          */
    ui->hopsTable->setColumnWidth(4,  55);     /* Alpha%	*/
    ui->hopsTable->setColumnWidth(5,  75);     /* Added         */
    ui->hopsTable->setColumnWidth(6,  65);     /* Time		*/
    ui->hopsTable->setColumnWidth(7,  50);     /* IBU		*/
    ui->hopsTable->setColumnWidth(8,  80);     /* Amount	*/
    ui->hopsTable->setColumnWidth(9,  80);     /* Stock		*/
    ui->hopsTable->setColumnWidth(10, 80);     /* Delete        */
    ui->hopsTable->setColumnWidth(11, 80);     /* Edit          */
    ui->hopsTable->setHorizontalHeaderLabels(labels);
    ui->hopsTable->verticalHeader()->hide();
    ui->hopsTable->setRowCount(product->hops.size());

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

	ui->hopsTable->setItem(i, 0, new QTableWidgetItem(product->hops.at(i).h_origin));
	ui->hopsTable->setItem(i, 1, new QTableWidgetItem(product->hops.at(i).h_name));

	item = new QTableWidgetItem(hop_types[product->hops.at(i).h_type]);
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        ui->hopsTable->setItem(i, 2, item);

	item = new QTableWidgetItem(hop_forms[product->hops.at(i).h_form]);
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        ui->hopsTable->setItem(i, 3, item);

	item = new QTableWidgetItem(QString("%1%").arg(product->hops.at(i).h_alpha, 2, 'f', 1, '0'));
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        ui->hopsTable->setItem(i, 4, item);

	item = new QTableWidgetItem(hop_useat[product->hops.at(i).h_useat]);
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        ui->hopsTable->setItem(i, 5, item);

	if (product->hops.at(i).h_useat == 2 || product->hops.at(i).h_useat == 4) {	// Boil or whirlpool
	    item = new QTableWidgetItem(QString("%1 min.").arg(product->hops.at(i).h_time, 1, 'f', 0, '0'));
	} else if (product->hops.at(i).h_useat == 5) {					// Dry-hop
	    item = new QTableWidgetItem(QString("%1 days.").arg(product->hops.at(i).h_time / 1440, 1, 'f', 0, '0'));
	} else {
	    item = new QTableWidgetItem(QString(""));
	}
	item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        ui->hopsTable->setItem(i, 6, item);

	double ibu = Utils::toIBU(product->hops.at(i).h_useat, product->hops.at(i).h_form, product->preboil_sg, product->batch_size,
			   product->hops.at(i).h_amount, product->hops.at(i).h_time, product->hops.at(i).h_alpha, product->ibu_method,
			   0, product->hops.at(i).h_time, 0, product->boil_time);
	item = new QTableWidgetItem(QString("%1").arg(ibu, 2, 'f', 1, '0'));
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        ui->hopsTable->setItem(i, 7, item);

	if (product->hops.at(i).h_amount < 1.0) {
	    item = new QTableWidgetItem(QString("%1 gr").arg(product->hops.at(i).h_amount * 1000.0, 2, 'f', 1, '0'));
	} else {
	    item = new QTableWidgetItem(QString("%1 kg").arg(product->hops.at(i).h_amount, 4, 'f', 3, '0'));
	}
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        ui->hopsTable->setItem(i, 8, item);

	if (product->hops.at(i).h_inventory < 1.0) {
            item = new QTableWidgetItem(QString("%1 gr").arg(product->hops.at(i).h_inventory * 1000.0, 2, 'f', 1, '0'));
        } else {
            item = new QTableWidgetItem(QString("%1 kg").arg(product->hops.at(i).h_inventory, 4, 'f', 3, '0'));
        }
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
	if (product->hops.at(i).h_inventory < product->hops.at(i).h_amount)
	    item->setForeground(QBrush(QColor(Qt::red)));
        ui->hopsTable->setItem(i, 9, item);

	/* Add the Delete row button */
        pWidget = new QWidget();
        QPushButton* btn_dele = new QPushButton();
        btn_dele->setObjectName(QString("%1").arg(i));  /* Send row with the button */
        btn_dele->setText(tr("Delete"));
        connect(btn_dele, SIGNAL(clicked()), this, SLOT(deleteHopRow_clicked()));
        pLayout = new QHBoxLayout(pWidget);
        pLayout->addWidget(btn_dele);
        pLayout->setContentsMargins(5, 0, 5, 0);
        pWidget->setLayout(pLayout);
        ui->hopsTable->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(editHopRow_clicked()));
        pLayout = new QHBoxLayout(pWidget);
        pLayout->addWidget(btn_edit);
        pLayout->setContentsMargins(5, 0, 5, 0);
        pWidget->setLayout(pLayout);
        ui->hopsTable->setCellWidget(i, 11, pWidget);
    }
}


void EditProduct::hop_Flavour_valueChanged(int value)
{
    if (value < 20) {
        ui->hop_tasteShow->setStyleSheet(bar_20);
	ui->hop_tasteShow->setFormat(tr("Very low"));
    } else if (value < 40) {
	ui->hop_tasteShow->setStyleSheet(bar_40);
	ui->hop_tasteShow->setFormat(tr("Low"));
    } else if (value < 60) {
        ui->hop_tasteShow->setStyleSheet(bar_60);
        ui->hop_tasteShow->setFormat(tr("Moderate"));
    } else if (value < 80) {
        ui->hop_tasteShow->setStyleSheet(bar_80);
        ui->hop_tasteShow->setFormat(tr("High"));
    } else {
        ui->hop_tasteShow->setStyleSheet(bar_100);
	ui->hop_tasteShow->setFormat(tr("Very high"));
    }
}


void EditProduct::hop_Aroma_valueChanged(int value)
{
    if (value < 20) {
        ui->hop_aromaShow->setStyleSheet(bar_20);
        ui->hop_aromaShow->setFormat(tr("Very low"));
    } else if (value < 40) {
        ui->hop_aromaShow->setStyleSheet(bar_40);
        ui->hop_aromaShow->setFormat(tr("Low"));
    } else if (value < 60) {
        ui->hop_aromaShow->setStyleSheet(bar_60);
        ui->hop_aromaShow->setFormat(tr("Moderate"));
    } else if (value < 80) {
        ui->hop_aromaShow->setStyleSheet(bar_80);
        ui->hop_aromaShow->setFormat(tr("High"));
    } else {
        ui->hop_aromaShow->setStyleSheet(bar_100);
        ui->hop_aromaShow->setFormat(tr("Very high"));
    }
}


void EditProduct::calcIBUs()
{
    double hop_flavour = 0, hop_aroma = 0, ibus = 0, ferm_ibus = 0;

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

	ibus += Utils::toIBU(product->hops.at(i).h_useat, product->hops.at(i).h_form, product->preboil_sg,
			   product->batch_size, product->hops.at(i).h_amount,
                           product->hops.at(i).h_time, product->hops.at(i).h_alpha, product->ibu_method,
			   product->brew_whirlpool9, product->brew_whirlpool7, product->brew_whirlpool6, product->boil_time);
	ferm_ibus += Utils::toIBU(product->hops.at(i).h_useat, product->hops.at(i).h_form, product->preboil_sg,
			   product->brew_fermenter_volume + product->brew_fermenter_tcloss, product->hops.at(i).h_amount,
                           product->hops.at(i).h_time, product->hops.at(i).h_alpha, product->ibu_method,
			   product->brew_whirlpool9, product->brew_whirlpool7, product->brew_whirlpool6, product->boil_time);

	hop_flavour += Utils::hopFlavourContribution(product->hops.at(i).h_time, product->batch_size, product->hops.at(i).h_useat, product->hops.at(i).h_amount);
        hop_aroma += Utils::hopAromaContribution(product->hops.at(i).h_time, product->batch_size, product->hops.at(i).h_useat, product->hops.at(i).h_amount);
	if ((((product->inventory_reduced <= PROD_STAGE_BREW)    && (product->hops.at(i).h_useat <= HOP_USEAT_WHIRLPOOL)) ||
             ((product->inventory_reduced <= PROD_STAGE_PACKAGE) && (product->hops.at(i).h_useat == HOP_USEAT_DRY_HOP))) &&
              (product->hops.at(i).h_inventory < product->hops.at(i).h_amount))
	    product->hops_ok = false;
    }

    hop_flavour = round(hop_flavour * 1000.0 / 5.0) / 10;
    hop_aroma = round(hop_aroma * 1000.0 / 6.0) / 10;
    if (hop_flavour > 100)
        hop_flavour = 100;
    if (hop_aroma > 100)
        hop_aroma = 100;
    qDebug() << "ibu" << product->est_ibu << ibus << "flavour" << hop_flavour << "aroma" << hop_aroma << "method" << product->ibu_method
	     << "fermenter" << ferm_ibus << "supplies" << product->hops_ok;

    product->est_ibu = ibus;
    product->brew_fermenter_ibu = ferm_ibus;
    ui->est_ibuEdit->setValue(product->est_ibu);
    ui->est_ibu2Edit->setValue(product->est_ibu);
    ui->est_ibuShow->setValue(product->est_ibu);
    ui->brew_fermentibuShow->setValue(product->brew_fermenter_ibu);
    ui->hop_tasteShow->setValue(hop_flavour);
    ui->hop_aromaShow->setValue(hop_aroma);
}


void EditProduct::addHopRow_clicked()
{
    Hops newh;

    qDebug() << "Add hop row";

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

    newh.h_name = "Select one";
    newh.h_origin = "";
    newh.h_amount = 0;
    newh.h_cost = 0;
    newh.h_type = HOP_TYPE_BITTERING;
    newh.h_form = HOP_FORMS_PELLET;
    newh.h_useat = HOP_USEAT_BOIL;
    newh.h_time = 0;
    newh.h_alpha = 0;
    newh.h_beta = 0;
    newh.h_hsi = 0;
    newh.h_humulene = 0;
    newh.h_caryophyllene = 0;
    newh.h_cohumulone = 0;
    newh.h_myrcene = 0;
    newh.h_total_oil = 0;
    newh.h_inventory = 0;

    product->hops.append(newh);
    emit refreshAll();
}


void EditProduct::deleteHopRow_clicked()
{
    if (product->locked || product->hops.size() < 1)
	return;

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

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

    product->hops.removeAt(row);
    is_changed();
    emit refreshAll();
}


void EditProduct::hop_amount_changed(double val)
{
    QTableWidgetItem *item;

    qDebug() << "hop_amount_changed()" << product->hops_row << val;

    product->hops[product->hops_row].h_amount = val / 1000.0;
    item = new QTableWidgetItem(QString("%1 gr").arg(val, 2, 'f', 1, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->hopsTable->setItem(product->hops_row, 8, item);

    double ibu = Utils::toIBU(product->hops.at(product->hops_row).h_useat, product->hops.at(product->hops_row).h_form, product->preboil_sg,
                              product->batch_size, product->hops.at(product->hops_row).h_amount, product->hops.at(product->hops_row).h_time,
                              product->hops.at(product->hops_row).h_alpha, product->ibu_method, 0, product->hops.at(product->hops_row).h_time, 0,
			      product->boil_time);

    ibuEdit->setValue(ibu);
    item = new QTableWidgetItem(QString("%1").arg(ibu, 2, 'f', 1, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->hopsTable->setItem(product->hops_row, 7, item);

    calcIBUs();
    is_changed();
}


void EditProduct::hop_time_changed(int val)
{
    QTableWidgetItem *item;

    qDebug() << "hop_time_changed()" << product->hops_row << val;

    if (product->hops.at(product->hops_row).h_useat == HOP_USEAT_BOIL || product->hops.at(product->hops_row).h_useat == HOP_USEAT_WHIRLPOOL) {
        item = new QTableWidgetItem(QString("%1 min.").arg(val, 1, 'f', 0, '0'));
	product->hops[product->hops_row].h_time = val;
    } else if (product->hops.at(product->hops_row).h_useat == HOP_USEAT_DRY_HOP) {
        item = new QTableWidgetItem(QString("%1 days.").arg(val, 1, 'f', 0, '0'));
	product->hops[product->hops_row].h_time = val * 1440;
    } else {
        item = new QTableWidgetItem(QString(""));
	product->hops[product->hops_row].h_time = val;
    }
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->hopsTable->setItem(product->hops_row, 6, item);

    double ibu = Utils::toIBU(product->hops.at(product->hops_row).h_useat, product->hops.at(product->hops_row).h_form, product->preboil_sg,
                              product->batch_size, product->hops.at(product->hops_row).h_amount, product->hops.at(product->hops_row).h_time,
 			      product->hops.at(product->hops_row).h_alpha, product->ibu_method,
			      product->brew_whirlpool9, product->brew_whirlpool7, product->brew_whirlpool6, product->boil_time);

    ibuEdit->setValue(ibu);
    item = new QTableWidgetItem(QString("%1").arg(ibu, 2, 'f', 1, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->hopsTable->setItem(product->hops_row, 7, item);

    calcIBUs();
    is_changed();
}


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

    if (val < 1)
        return;

    qDebug() << "hop_select_changed()" << product->fermentables_row << val << instock;

    /*
     * Search the hop pointed by the index and instock flag.
     */
    QString sql = "SELECT name,origin,alpha,beta,humulene,caryophyllene,cohumulone,myrcene,hsi,total_oil,type,form,cost,inventory FROM inventory_hops ";
    if (instock)
        sql.append("WHERE inventory > 0 ");
    sql.append("ORDER BY origin,name");
    query.prepare(sql);
    query.exec();
    query.first();
    for (int i = 0; i < (val - 1); i++) {
        query.next();
    }

    /*
     * Replace the hop record contents
     */
    product->hops[product->hops_row].h_name = query.value(0).toString();
    product->hops[product->hops_row].h_origin = query.value(1).toString();
    product->hops[product->hops_row].h_alpha = query.value(2).toDouble();
    product->hops[product->hops_row].h_beta = query.value(3).toDouble();
    product->hops[product->hops_row].h_humulene = query.value(4).toDouble();
    product->hops[product->hops_row].h_caryophyllene = query.value(5).toDouble();
    product->hops[product->hops_row].h_cohumulone = query.value(6).toDouble();
    product->hops[product->hops_row].h_myrcene = query.value(7).toDouble();
    product->hops[product->hops_row].h_hsi = query.value(8).toDouble();
    product->hops[product->hops_row].h_total_oil = query.value(9).toDouble();
    product->hops[product->hops_row].h_type = query.value(10).toInt();
    product->hops[product->hops_row].h_form = query.value(11).toInt();
    product->hops[product->hops_row].h_cost = query.value(12).toDouble();
    product->hops[product->hops_row].h_inventory = query.value(13).toDouble();

    /*
     * Update the visible fields
     */
    hnameEdit->setText(product->hops.at(product->hops_row).h_name);
    horiginEdit->setText(product->hops.at(product->hops_row).h_origin);

    double ibu = Utils::toIBU(product->hops.at(product->hops_row).h_useat, product->hops.at(product->hops_row).h_form, product->preboil_sg,
		              product->batch_size, product->hops.at(product->hops_row).h_amount, product->hops.at(product->hops_row).h_time,
			      product->hops.at(product->hops_row).h_alpha, product->ibu_method,
			      product->brew_whirlpool9, product->brew_whirlpool7, product->brew_whirlpool6, product->boil_time);
    ibuEdit->setValue(ibu);

    ui->hopsTable->setItem(product->hops_row, 0, new QTableWidgetItem(product->hops.at(product->hops_row).h_origin));
    ui->hopsTable->setItem(product->hops_row, 1, new QTableWidgetItem(product->hops.at(product->hops_row).h_name));

    item = new QTableWidgetItem(hop_types[product->hops.at(product->hops_row).h_type]);
    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
    ui->hopsTable->setItem(product->hops_row, 2, item);

    item = new QTableWidgetItem(hop_forms[product->hops.at(product->hops_row).h_form]);
    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
    ui->hopsTable->setItem(product->hops_row, 3, item);

    item = new QTableWidgetItem(QString("%1%").arg(product->hops.at(product->hops_row).h_alpha, 2, 'f', 1, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->hopsTable->setItem(product->hops_row, 4, item);

    item = new QTableWidgetItem(QString("%1").arg(ibu, 2, 'f', 1, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->hopsTable->setItem(product->hops_row, 7, item);

    if (product->hops.at(product->hops_row).h_inventory < 1.0) {
	item = new QTableWidgetItem(QString("%1 gr").arg(product->hops.at(product->hops_row).h_inventory * 1000.0, 2, 'f', 1, '0'));
    } else {
	item = new QTableWidgetItem(QString("%1 kg").arg(product->hops.at(product->hops_row).h_inventory, 4, 'f', 3, '0'));
    }
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    if (product->hops.at(product->hops_row).h_inventory < product->hops.at(product->hops_row).h_amount)
	item->setForeground(QBrush(QColor(Qt::red)));
    ui->hopsTable->setItem(product->hops_row, 9, item);

    calcIBUs();
    is_changed();
}


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

    qDebug() << "hop_instock_changed()" << product->hops_row << val;

    this->hselectEdit->setCurrentIndex(-1);
    this->hselectEdit->clear();
    QString sql = "SELECT origin,name,alpha,inventory FROM inventory_hops ";
    if (val)
        sql.append("WHERE inventory > 0 ");
    sql.append("ORDER BY origin,name");
    query.prepare(sql);
    query.exec();
    query.first();
    this->hselectEdit->addItem("");      // Start with empty value
    for (int i = 0; i < query.size(); i++) {
        this->hselectEdit->addItem(query.value(0).toString()+" - "+query.value(1).toString()+" ("+query.value(2).toString()+"%) "+
                        QString("%1 gr").arg(query.value(3).toDouble() * 1000.0, 2, 'f', 1, '0'));
        query.next();
    }
}


void EditProduct::hop_useat_changed(int val)
{
    qDebug() << "hop_useat_changed()" << product->hops_row << val;

    product->hops[product->hops_row].h_useat = val;
    QTableWidgetItem *item = new QTableWidgetItem(hop_useat[val]);
    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
    ui->hopsTable->setItem(product->hops_row, 5, item);

    if (val == HOP_USEAT_BOIL || val == HOP_USEAT_WHIRLPOOL) {
	htimeLabel->setText(tr("Time in minutes:"));
        htimeEdit->setValue(product->hops.at(product->hops_row).h_time);
	htimeEdit->setReadOnly(false);
    } else if (val == HOP_USEAT_DRY_HOP) {
        htimeLabel->setText(tr("Time in days:"));
	htimeEdit->setValue(product->hops.at(product->hops_row).h_time / 1440);
	htimeEdit->setReadOnly(false);
    } else {
        htimeLabel->setText("");
	htimeEdit->setValue(0);
	htimeEdit->setReadOnly(true);
    }

    is_changed();
    emit refreshAll();
}


void EditProduct::editHopRow_clicked()
{
    QSqlQuery query;

    if (product->locked)
	return;

    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
    product->hops_row = pb->objectName().toInt();
    qDebug() << "Edit hop row" << product->hops_row;
    Hops backup = product->hops.at(product->hops_row);

    QDialog* dialog = new QDialog(this);
    dialog->resize(738, 260);
    QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
    buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
    buttonBox->setGeometry(QRect(30, 210, 671, 32));
    buttonBox->setLayoutDirection(Qt::LeftToRight);
    buttonBox->setOrientation(Qt::Horizontal);
    buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
    buttonBox->setCenterButtons(true);
    QLabel *nameLabel = new QLabel(dialog);
    nameLabel->setObjectName(QString::fromUtf8("nameLabel"));
    nameLabel->setText(tr("Current hop:"));
    nameLabel->setGeometry(QRect(10, 10, 141, 20));
    nameLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *originLabel = new QLabel(dialog);
    originLabel->setObjectName(QString::fromUtf8("originLabel"));
    originLabel->setText(tr("Origin:"));
    originLabel->setGeometry(QRect(10, 40, 141, 20));
    originLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *amountLabel = new QLabel(dialog);
    amountLabel->setObjectName(QString::fromUtf8("amountLabel"));
    amountLabel->setText(tr("Amount in gr:"));
    amountLabel->setGeometry(QRect(10, 100, 141, 20));
    amountLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    htimeLabel = new QLabel(dialog);
    htimeLabel->setObjectName(QString::fromUtf8("htimeLabel"));
    if (product->hops.at(product->hops_row).h_useat == 5)		// Dry-hop
	htimeLabel->setText(tr("Time in days:"));
    else if (product->hops.at(product->hops_row).h_useat == 2 || product->hops.at(product->hops_row).h_useat == 4)	// Boil or whirlpool
    	htimeLabel->setText(tr("Time in minutes:"));
    else
	htimeLabel->setText("");

    htimeLabel->setGeometry(QRect(10, 130, 141, 20));
    htimeLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *useatLabel = new QLabel(dialog);
    useatLabel->setObjectName(QString::fromUtf8("useatLabel"));
    useatLabel->setText(tr("Use at:"));
    useatLabel->setGeometry(QRect(10, 160, 141, 20));
    useatLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *selectLabel = new QLabel(dialog);
    selectLabel->setObjectName(QString::fromUtf8("selectLabel"));
    selectLabel->setText(tr("Select hop:"));
    selectLabel->setGeometry(QRect(10, 70, 141, 20));
    selectLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *instockLabel = new QLabel(dialog);
    instockLabel->setObjectName(QString::fromUtf8("instockLabel"));
    instockLabel->setText(tr("In stock:"));
    instockLabel->setGeometry(QRect(525, 70, 121, 20));
    instockLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *ibuLabel = new QLabel(dialog);
    ibuLabel->setObjectName(QString::fromUtf8("maxLabel"));
    ibuLabel->setText(tr("Bitterness IBU:"));
    ibuLabel->setGeometry(QRect(420, 130, 121, 20));
    ibuLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);

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

    hnameEdit = new QLineEdit(dialog);
    hnameEdit->setObjectName(QString::fromUtf8("hnameEdit"));
    hnameEdit->setText(product->hops.at(product->hops_row).h_name);
    hnameEdit->setGeometry(QRect(160, 10, 511, 23));
    hnameEdit->setReadOnly(true);
    horiginEdit = new QLineEdit(dialog);
    horiginEdit->setObjectName(QString::fromUtf8("horiginEdit"));
    horiginEdit->setText(product->hops.at(product->hops_row).h_origin);
    horiginEdit->setGeometry(QRect(160, 40, 511, 23));
    horiginEdit->setReadOnly(true);
    hamountEdit = new QDoubleSpinBox(dialog);
    hamountEdit->setObjectName(QString::fromUtf8("hamountEdit"));
    hamountEdit->setGeometry(QRect(160, 100, 121, 24));
    hamountEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    hamountEdit->setAccelerated(true);
    hamountEdit->setDecimals(1);
    hamountEdit->setMaximum(1000000.0);
    hamountEdit->setSingleStep(0.5);
    hamountEdit->setValue(product->hops.at(product->hops_row).h_amount * 1000.0);
    htimeEdit = new QSpinBox(dialog);
    htimeEdit->setObjectName(QString::fromUtf8("htimeEdit"));
    htimeEdit->setGeometry(QRect(160, 130, 121, 24));
    htimeEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    htimeEdit->setAccelerated(true);
    htimeEdit->setMaximum(10000.0);
    if (product->hops.at(product->hops_row).h_useat == HOP_USEAT_BOIL || product->hops.at(product->hops_row).h_useat == HOP_USEAT_WHIRLPOOL) {
	htimeEdit->setValue(product->hops.at(product->hops_row).h_time);
	htimeEdit->setReadOnly(false);
    } else if (product->hops.at(product->hops_row).h_useat == HOP_USEAT_DRY_HOP) {
	htimeEdit->setValue(product->hops.at(product->hops_row).h_time / 1440);
	htimeEdit->setReadOnly(false);
    } else {
	htimeEdit->setReadOnly(true);
    }
    useatEdit = new QComboBox(dialog);
    useatEdit->setObjectName(QString::fromUtf8("useatEdit"));
    useatEdit->setGeometry(QRect(160, 160, 161, 23));
    useatEdit->addItem(tr("Mash"));
    useatEdit->addItem(tr("First wort"));
    useatEdit->addItem(tr("Boil"));
    useatEdit->addItem(tr("Aroma"));
    useatEdit->addItem(tr("Whirlpool"));
    useatEdit->addItem(tr("Dry hop"));
    useatEdit->setCurrentIndex(product->hops.at(product->hops_row).h_useat);

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

    ibuEdit = new QDoubleSpinBox(dialog);
    ibuEdit->setObjectName(QString::fromUtf8("ibuEdit"));
    ibuEdit->setGeometry(QRect(550, 130, 121, 24));
    ibuEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    ibuEdit->setReadOnly(true);
    ibuEdit->setButtonSymbols(QAbstractSpinBox::NoButtons);
    ibuEdit->setDecimals(1);
    double ibu = Utils::toIBU(product->hops.at(product->hops_row).h_useat, product->hops.at(product->hops_row).h_form, product->preboil_sg,
                              product->batch_size, product->hops.at(product->hops_row).h_amount, product->hops.at(product->hops_row).h_time,
                              product->hops.at(product->hops_row).h_alpha, product->ibu_method,
			      product->brew_whirlpool9, product->brew_whirlpool7, product->brew_whirlpool6, product->boil_time);
    ibuEdit->setValue(ibu);

    hop_instock_changed(true);

    connect(hselectEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::hop_select_changed);
    connect(hamountEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::hop_amount_changed);
    connect(htimeEdit, QOverload<int>::of(&QSpinBox::valueChanged), this, &EditProduct::hop_time_changed);
    connect(useatEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::hop_useat_changed);
    connect(hinstockEdit, &QCheckBox::stateChanged, this, &EditProduct::hop_instock_changed);
    connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));
    connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));

    dialog->setModal(true);
    dialog->exec();
    if (dialog->result() == QDialog::Rejected) {
        qDebug() << "reject and rollback";
        product->hops[product->hops_row] = backup;
    } else {
	/* Clear time if hop is not used for boil, whirlpool or dry-hop. */
	if (! (product->hops.at(product->hops_row).h_useat == HOP_USEAT_BOIL ||
	       product->hops.at(product->hops_row).h_useat == HOP_USEAT_WHIRLPOOL ||
	       product->hops.at(product->hops_row).h_useat == HOP_USEAT_DRY_HOP)) {
	    if (product->hops.at(product->hops_row).h_time) {
		product->hops[product->hops_row].h_time = 0;
		is_changed();
	    }
	}
    }

    disconnect(hselectEdit, nullptr, nullptr, nullptr);
    disconnect(hamountEdit, nullptr, nullptr, nullptr);
    disconnect(htimeEdit, nullptr, nullptr, nullptr);
    disconnect(useatEdit, nullptr, nullptr, nullptr);
    disconnect(hinstockEdit, nullptr, nullptr, nullptr);
    disconnect(buttonBox, nullptr, nullptr, nullptr);

    emit refreshAll();
}


void EditProduct::adjustHops(double factor)
{
    double amount;

    if (product->hops.size() == 0)
	return;

    for (int i = 0; i < product->hops.size(); i++) {
	amount = product->hops.at(i).h_amount * factor;
	product->hops[i].h_amount = amount;
    }
}

mercurial