src/EditProductTab4.cpp

changeset 175
f1ed3a2a94e9
child 178
1091fd9feffe
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EditProductTab4.cpp	Thu Apr 28 22:49:13 2022 +0200
@@ -0,0 +1,636 @@
+/**
+ * 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("Delete"), tr("Edit") });
+
+    ui->hopsTable->setColumnCount(11);
+    ui->hopsTable->setColumnWidth(0, 150);     /* Origin	*/
+    ui->hopsTable->setColumnWidth(1, 225);     /* Hop		*/
+    ui->hopsTable->setColumnWidth(2,  84);     /* Type		*/
+    ui->hopsTable->setColumnWidth(3,  84);     /* Form          */
+    ui->hopsTable->setColumnWidth(4,  75);     /* Alpha%	*/
+    ui->hopsTable->setColumnWidth(5,  75);     /* Added         */
+    ui->hopsTable->setColumnWidth(6,  75);     /* Time		*/
+    ui->hopsTable->setColumnWidth(7,  60);     /* IBU		*/
+    ui->hopsTable->setColumnWidth(8,  90);     /* Amount	*/
+    ui->hopsTable->setColumnWidth(9,  80);     /* Delete        */
+    ui->hopsTable->setColumnWidth(10, 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);
+	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);
+
+	/* 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, 9, 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, 10, 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;
+
+    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, 0, product->hops.at(i).h_time, 0);
+	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);
+    }
+
+    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;
+
+    product->est_ibu = ibus;
+    ui->est_ibuEdit->setValue(product->est_ibu);
+    ui->est_ibu2Edit->setValue(product->est_ibu);
+    ui->est_ibuShow->setValue(product->est_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 = 0;
+    newh.h_form = 0;
+    newh.h_useat = 2;
+    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;
+
+    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);
+
+    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 == 2 || product->hops.at(product->hops_row).h_useat == 4) {       // Boil or 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 == 5) {                                   // 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, 0, product->hops.at(product->hops_row).h_time, 0);
+
+    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 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();
+    }
+    qDebug() << "found" << query.value(1).toString() << query.value(0).toString();
+
+    /*
+     * 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();
+
+    /*
+     * 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, 0, product->hops.at(product->hops_row).h_time, 0);
+    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);
+
+    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 == 2 || val == 4) {	// Boil or whirlpool
+	htimeLabel->setText(tr("Time in minutes:"));
+        htimeEdit->setValue(product->hops.at(product->hops_row).h_time);
+	htimeEdit->setReadOnly(false);
+    } else if (val == 5) {	// 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 == 2 || product->hops.at(product->hops_row).h_useat == 4) {	// Boil or whirlpool
+	htimeEdit->setValue(product->hops.at(product->hops_row).h_time);
+	htimeEdit->setReadOnly(false);
+    } else if (product->hops.at(product->hops_row).h_useat == 5){	// 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, 0, product->hops.at(product->hops_row).h_time, 0);
+    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 == 2 ||
+	       product->hops.at(product->hops_row).h_useat == 4 ||
+	       product->hops.at(product->hops_row).h_useat == 5)) {
+	    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