src/EditProductTab4.cpp

Thu, 18 Aug 2022 19:47:28 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Thu, 18 Aug 2022 19:47:28 +0200
changeset 399
4b9aaf86094e
parent 398
49cf387e9070
child 413
8ea791d4cab6
permissions
-rw-r--r--

Prompts corrections and updated translations. Minor layout changes so the prompts will fit.

/**
 * 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.useat > D2.useat)
	return false;
    if (D1.useat < D2.useat)
	return true;
    /* Same useat moments, test time. */
    if (D1.time < D2.time)
	return false;
    if (D1.time > D2.time)
	return true;
    /* Finally consider the amounts */
    return (D1.amount > D2.amount);
}


bool EditProduct::block_hop(int stage, int useat)
{
    if (stage > PROD_STAGE_BREW && useat < HOP_USEAT_DRY_HOP)
	return true;
    return false;
}


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).origin));
	ui->hopsTable->setItem(i, 1, new QTableWidgetItem(product->hops.at(i).name));

    	item = new QTableWidgetItem(QCoreApplication::translate("HopTypes", g_hop_types[product->hops.at(i).type]));
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        ui->hopsTable->setItem(i, 2, item);

	item = new QTableWidgetItem(QCoreApplication::translate("HopForm", g_hop_forms[product->hops.at(i).form]));
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        ui->hopsTable->setItem(i, 3, item);

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

	item = new QTableWidgetItem(QCoreApplication::translate("HopUse", g_hop_useat[product->hops.at(i).useat]));
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        ui->hopsTable->setItem(i, 5, item);

	if (product->hops.at(i).useat == 2 || product->hops.at(i).useat == 4) {	// Boil or whirlpool
	    item = new QTableWidgetItem(QString("%1 min.").arg(product->hops.at(i).time, 1, 'f', 0, '0'));
	} else if (product->hops.at(i).useat == 5) {					// Dry-hop
	    item = new QTableWidgetItem(QString("%1 days.").arg(product->hops.at(i).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).useat, product->hops.at(i).form, product->preboil_sg, product->est_og3, product->batch_size,
			   product->hops.at(i).amount, product->hops.at(i).time, product->hops.at(i).alpha, product->ibu_method,
			   product->brew_whirlpool9, product->brew_whirlpool7, product->brew_whirlpool6, product->boil_time,
			   product->brew_cooling_method, 0, 0, product->hops.at(i).utilisation, product->hops.at(i).bu_factor);
	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).amount < 1.0) {
	    item = new QTableWidgetItem(QString("%1 gr").arg(product->hops.at(i).amount * 1000.0, 2, 'f', 1, '0'));
	} else {
	    item = new QTableWidgetItem(QString("%1 kg").arg(product->hops.at(i).amount, 4, 'f', 3, '0'));
	}
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        ui->hopsTable->setItem(i, 8, item);

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

	/* Add the Delete row button */
	if (block_hop(product->stage, product->hops.at(i).useat)) {
	    item = new QTableWidgetItem("");
            item->setToolTip(tr("Hop already used"));
            ui->hopsTable->setItem(i, 10, item);
            item = new QTableWidgetItem("");
            item->setToolTip(tr("Hop already used"));
            ui->hopsTable->setItem(i, 11, item);
	} else {
            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 {
        double s1 = 20.0 / value;
        if (value < 40) {
            ui->hop_tasteShow->setStyleSheet(QString("QProgressBar::chunk {background-color: qlineargradient(x0: 0, x2: 1, "
                  "stop: 0 #004D00, stop: %1 #004D00, stop: %2 #008C00, stop: 1 #008C00);}").arg(s1).arg(s1));
            ui->hop_tasteShow->setFormat(tr("Low"));
        } else {
            double s2 = 40.0 / value;
            if (value < 60) {
                ui->hop_tasteShow->setStyleSheet(QString("QProgressBar::chunk {background-color: qlineargradient(x0: 0, x2: 1, "
                  "stop: 0 #004D00, stop: %1 #004D00, stop: %2 #006C00, stop: %3 #006C00, stop: %4 #008C00, stop: 1 #008C00"
                  ");}").arg(s1).arg(s1).arg(s2).arg(s2));
                ui->hop_tasteShow->setFormat(tr("Moderate"));
            } else {
                double s3 = 60.0 / value;
                if (value < 80) {
                    ui->hop_tasteShow->setStyleSheet(QString("QProgressBar::chunk {background-color: qlineargradient(x0: 0, x2: 1, "
                      "stop: 0 #004D00, stop: %1 #004D00, stop: %2 #006C00, stop: %3 #006C00, stop: %4 #008C00, stop: %5 #008C00, "
                      "stop: %6 #00AC00, stop: 1 #00AC00"
                      ");}").arg(s1).arg(s1).arg(s2).arg(s2).arg(s3).arg(s3));
                    ui->hop_tasteShow->setFormat(tr("High"));
                } else {
                    double s4 = 80.0 / value;
                    ui->hop_tasteShow->setStyleSheet(QString("QProgressBar::chunk {background-color: qlineargradient(x0: 0, x2: 1, "
                      "stop: 0 #004D00, stop: %1 #004D00, stop: %2 #006C00, stop: %3 #006C00, stop: %4 #008C00, stop: %5 #008C00, "
                      "stop: %6 #00AC00, stop: %7 #00AC00, stop: %8 #00CC00, stop: 1 #00CC00"
                      ");}").arg(s1).arg(s1).arg(s2).arg(s2).arg(s3).arg(s3).arg(s4).arg(s4));
                    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 {
	double s1 = 20.0 / value;
	if (value < 40) {
	    ui->hop_aromaShow->setStyleSheet(QString("QProgressBar::chunk {background-color: qlineargradient(x0: 0, x2: 1, "
                  "stop: 0 #004D00, stop: %1 #004D00, stop: %2 #008C00, stop: 1 #008C00);}").arg(s1).arg(s1));
	    ui->hop_aromaShow->setFormat(tr("Low"));
	} else {
	    double s2 = 40.0 / value;
	    if (value < 60) {
		ui->hop_aromaShow->setStyleSheet(QString("QProgressBar::chunk {background-color: qlineargradient(x0: 0, x2: 1, "
                  "stop: 0 #004D00, stop: %1 #004D00, stop: %2 #006C00, stop: %3 #006C00, stop: %4 #008C00, stop: 1 #008C00"
                  ");}").arg(s1).arg(s1).arg(s2).arg(s2));
		ui->hop_aromaShow->setFormat(tr("Moderate"));
	    } else {
		double s3 = 60.0 / value;
		if (value < 80) {
		    ui->hop_aromaShow->setStyleSheet(QString("QProgressBar::chunk {background-color: qlineargradient(x0: 0, x2: 1, "
                      "stop: 0 #004D00, stop: %1 #004D00, stop: %2 #006C00, stop: %3 #006C00, stop: %4 #008C00, stop: %5 #008C00, "
		      "stop: %6 #00AC00, stop: 1 #00AC00"
                      ");}").arg(s1).arg(s1).arg(s2).arg(s2).arg(s3).arg(s3));
		    ui->hop_aromaShow->setFormat(tr("High"));
		} else {
		    double s4 = 80.0 / value;
		    ui->hop_aromaShow->setStyleSheet(QString("QProgressBar::chunk {background-color: qlineargradient(x0: 0, x2: 1, "
                      "stop: 0 #004D00, stop: %1 #004D00, stop: %2 #006C00, stop: %3 #006C00, stop: %4 #008C00, stop: %5 #008C00, "
                      "stop: %6 #00AC00, stop: %7 #00AC00, stop: %8 #00CC00, stop: 1 #00CC00"
                      ");}").arg(s1).arg(s1).arg(s2).arg(s2).arg(s3).arg(s3).arg(s4).arg(s4));
		    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).useat, product->hops.at(i).form, product->preboil_sg, product->est_og3,
			   product->batch_size, product->hops.at(i).amount,
                           product->hops.at(i).time, product->hops.at(i).alpha, product->ibu_method,
			   product->brew_whirlpool9, product->brew_whirlpool7, product->brew_whirlpool6, product->boil_time,
			   product->brew_cooling_method, 0, 0, product->hops.at(i).utilisation, product->hops.at(i).bu_factor);
	if (product->stage > PROD_STAGE_BREW)
	    ferm_ibus += Utils::toIBU(product->hops.at(i).useat, product->hops.at(i).form, product->brew_preboil_sg, product->brew_fermenter_sg,
                           product->brew_fermenter_volume + product->brew_fermenter_tcloss, product->hops.at(i).amount,
                           product->hops.at(i).time, product->hops.at(i).alpha, product->ibu_method,
                           product->brew_whirlpool9, product->brew_whirlpool7, product->brew_whirlpool6, product->boil_time,
                           product->brew_cooling_method, 0, 0, product->hops.at(i).utilisation, product->hops.at(i).bu_factor);
	else
	    ferm_ibus += Utils::toIBU(product->hops.at(i).useat, product->hops.at(i).form, product->preboil_sg, product->est_og3,
			   product->brew_fermenter_volume + product->brew_fermenter_tcloss, product->hops.at(i).amount,
                           product->hops.at(i).time, product->hops.at(i).alpha, product->ibu_method,
			   product->brew_whirlpool9, product->brew_whirlpool7, product->brew_whirlpool6, product->boil_time,
			   product->brew_cooling_method, 0, 0, product->hops.at(i).utilisation, product->hops.at(i).bu_factor);

	hop_flavour += Utils::hopFlavourContribution(product->hops.at(i).time, product->batch_size, product->hops.at(i).useat,
				product->hops.at(i).amount, product->hops.at(i).form);
        hop_aroma += Utils::hopAromaContribution(product->hops.at(i).time, product->batch_size, product->hops.at(i).useat,
				product->hops.at(i).amount, product->hops.at(i).form);
	if ((((product->inventory_reduced <= PROD_STAGE_BREW)    && (product->hops.at(i).useat <= HOP_USEAT_WHIRLPOOL)) ||
             ((product->inventory_reduced <= PROD_STAGE_PACKAGE) && (product->hops.at(i).useat == HOP_USEAT_DRY_HOP))) &&
              (product->hops.at(i).inventory < product->hops.at(i).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;
    if (product->stage > PROD_STAGE_BREW) {
	ui->est_ibuLabel->setText(tr("Final IBU:"));
	ui->est_ibuEdit->setValue(product->brew_fermenter_ibu);
	ui->est_ibuShow->setValue(product->brew_fermenter_ibu);
    } else {
	ui->est_ibuEdit->setValue(product->est_ibu);
	ui->est_ibuShow->setValue(product->est_ibu);
    }
    ui->est_ibu2Edit->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).amount == 0 && product->hops.at(i).alpha == 0)
            return;     // Add only one at a time.
    }

    newh.name = "Select one";
    newh.origin = "";
    newh.amount = 0;
    newh.cost = 0;
    newh.type = HOP_TYPE_BITTERING;
    newh.form = HOP_FORMS_PELLET;
    newh.useat = HOP_USEAT_BOIL;
    newh.time = 0;
    newh.alpha = 0;
    newh.beta = 0;
    newh.hsi = 0;
    newh.humulene = 0;
    newh.caryophyllene = 0;
    newh.cohumulone = 0;
    newh.myrcene = 0;
    newh.total_oil = 0;
    newh.inventory = 0;
    newh.utilisation = my_ut_pellet;
    newh.bu_factor = 1;

    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).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].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).useat, product->hops.at(product->hops_row).form, product->preboil_sg, product->est_og3,
                              product->batch_size, product->hops.at(product->hops_row).amount, product->hops.at(product->hops_row).time,
                              product->hops.at(product->hops_row).alpha, product->ibu_method,
			      product->brew_whirlpool9, product->brew_whirlpool7, product->brew_whirlpool6, product->boil_time,
			      product->brew_cooling_method, 0, 0,
			      product->hops.at(product->hops_row).utilisation, product->hops.at(product->hops_row).bu_factor);

    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).useat == HOP_USEAT_BOIL || product->hops.at(product->hops_row).useat == HOP_USEAT_WHIRLPOOL) {
        item = new QTableWidgetItem(QString("%1 min.").arg(val, 1, 'f', 0, '0'));
	product->hops[product->hops_row].time = val;
    } else if (product->hops.at(product->hops_row).useat == HOP_USEAT_DRY_HOP) {
        item = new QTableWidgetItem(QString("%1 days.").arg(val, 1, 'f', 0, '0'));
	product->hops[product->hops_row].time = val * 1440;
    } else {
        item = new QTableWidgetItem(QString(""));
	product->hops[product->hops_row].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).useat, product->hops.at(product->hops_row).form, product->preboil_sg, product->est_og3,
                              product->batch_size, product->hops.at(product->hops_row).amount, product->hops.at(product->hops_row).time,
 			      product->hops.at(product->hops_row).alpha, product->ibu_method,
			      product->brew_whirlpool9, product->brew_whirlpool7, product->brew_whirlpool6, product->boil_time,
			      product->brew_cooling_method, 0, 0,
			      product->hops.at(product->hops_row).utilisation, product->hops.at(product->hops_row).bu_factor);

    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 * 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].name = query.value("name").toString();
    product->hops[product->hops_row].origin = query.value("origin").toString();
    product->hops[product->hops_row].alpha = query.value("alpha").toDouble();
    product->hops[product->hops_row].beta = query.value("beta").toDouble();
    product->hops[product->hops_row].humulene = query.value("humulene").toDouble();
    product->hops[product->hops_row].caryophyllene = query.value("caryophyllene").toDouble();
    product->hops[product->hops_row].cohumulone = query.value("cohumulone").toDouble();
    product->hops[product->hops_row].myrcene = query.value("myrcene").toDouble();
    product->hops[product->hops_row].hsi = query.value("hsi").toDouble();
    product->hops[product->hops_row].total_oil = query.value("total_oil").toDouble();
    product->hops[product->hops_row].type = query.value("type").toInt();
    product->hops[product->hops_row].form = query.value("form").toInt();
    product->hops[product->hops_row].cost = query.value("cost").toDouble();
    product->hops[product->hops_row].inventory = query.value("inventory").toDouble();
    product->hops[product->hops_row].utilisation = query.value("utilisation").toDouble();
    product->hops[product->hops_row].bu_factor = query.value("bu_factor").toDouble();

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

    double ibu = Utils::toIBU(product->hops.at(product->hops_row).useat, product->hops.at(product->hops_row).form, product->preboil_sg, product->est_og3,
		              product->batch_size, product->hops.at(product->hops_row).amount, product->hops.at(product->hops_row).time,
			      product->hops.at(product->hops_row).alpha, product->ibu_method,
			      product->brew_whirlpool9, product->brew_whirlpool7, product->brew_whirlpool6, product->boil_time,
			      product->brew_cooling_method, 0, 0,
			      product->hops.at(product->hops_row).utilisation, product->hops.at(product->hops_row).bu_factor);
    ibuEdit->setValue(ibu);

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

    QString hoptype = QCoreApplication::translate("HopTypes", g_hop_types[product->hops.at(product->hops_row).type]);
    item = new QTableWidgetItem(hoptype);
    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
    ui->hopsTable->setItem(product->hops_row, 2, item);


    item = new QTableWidgetItem(QCoreApplication::translate("HopForm", g_hop_forms[product->hops.at(product->hops_row).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).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).inventory < 1.0) {
	item = new QTableWidgetItem(QString("%1 gr").arg(product->hops.at(product->hops_row).inventory * 1000.0, 2, 'f', 1, '0'));
    } else {
	item = new QTableWidgetItem(QString("%1 kg").arg(product->hops.at(product->hops_row).inventory, 4, 'f', 3, '0'));
    }
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    if (product->hops.at(product->hops_row).inventory < product->hops.at(product->hops_row).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].useat = val;
    QTableWidgetItem *item = new QTableWidgetItem(QCoreApplication::translate("HopUse", g_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).time);
	htimeLabel->show();
	htimeEdit->show();
    } else if (val == HOP_USEAT_DRY_HOP) {
        htimeLabel->setText(tr("Time in days:"));
	htimeEdit->setValue(product->hops.at(product->hops_row).time / 1440);
	htimeLabel->show();
        htimeEdit->show();
    } else {
	htimeEdit->setValue(0);
	htimeLabel->hide();
        htimeEdit->hide();
    }

    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).useat == HOP_USEAT_DRY_HOP)
	htimeLabel->setText(tr("Time in days:"));
    else
    	htimeLabel->setText(tr("Time in minutes:"));

    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).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).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).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).useat == HOP_USEAT_BOIL || product->hops.at(product->hops_row).useat == HOP_USEAT_WHIRLPOOL) {
	htimeEdit->setValue(product->hops.at(product->hops_row).time);
	htimeLabel->show();
	htimeEdit->show();
    } else if (product->hops.at(product->hops_row).useat == HOP_USEAT_DRY_HOP) {
	htimeEdit->setValue(product->hops.at(product->hops_row).time / 1440);
	htimeLabel->show();
        htimeEdit->show();
    } else {
	htimeLabel->hide();
        htimeEdit->hide();
    }
    useatEdit = new QComboBox(dialog);
    useatEdit->setObjectName(QString::fromUtf8("useatEdit"));
    useatEdit->setGeometry(QRect(160, 160, 161, 23));
    for (int i = 0; i < 7; i++)
	useatEdit->addItem(QCoreApplication::translate("HopUse", g_hop_useat[i]));
    useatEdit->setCurrentIndex(product->hops.at(product->hops_row).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).useat, product->hops.at(product->hops_row).form, product->preboil_sg, product->est_og3,
                              product->batch_size, product->hops.at(product->hops_row).amount, product->hops.at(product->hops_row).time,
                              product->hops.at(product->hops_row).alpha, product->ibu_method,
			      product->brew_whirlpool9, product->brew_whirlpool7, product->brew_whirlpool6, product->boil_time,
			      product->brew_cooling_method, 0, 0,
			      product->hops.at(product->hops_row).utilisation, product->hops.at(product->hops_row).bu_factor);
    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).useat == HOP_USEAT_BOIL ||
	       product->hops.at(product->hops_row).useat == HOP_USEAT_WHIRLPOOL ||
	       product->hops.at(product->hops_row).useat == HOP_USEAT_DRY_HOP)) {
	    if (product->hops.at(product->hops_row).time) {
		product->hops[product->hops_row].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).amount * factor;
	product->hops[i].amount = amount;
    }
}

mercurial