src/EditRecipe.cpp

Sun, 03 Apr 2022 22:02:12 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Sun, 03 Apr 2022 22:02:12 +0200
changeset 103
6da4e93b6ceb
parent 102
b017001850df
child 104
c02dfb7bb2f9
permissions
-rw-r--r--

Experiment with checkboxes in the fermentable table.

/**
 * EditRecipe.cpp is part of bmsapp.
 *
 * 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/>.
 */
#include "EditRecipe.h"
#include "../ui/ui_EditRecipe.h"
#include "MainWindow.h"
#include "Utils.h"


EditRecipe::EditRecipe(int id, QWidget *parent) : QDialog(parent), ui(new Ui::EditRecipe)
{
    QSqlQuery query;

    qDebug() << "EditRecipe record:" << id;
    ui->setupUi(this);
    this->recno = id;

    WindowTitle();

    ui->typeEdit->addItem(tr("Extract"));
    ui->typeEdit->addItem(tr("Partial Mash"));
    ui->typeEdit->addItem(tr("All Grain"));

    ui->color_methodEdit->addItem("Morey");
    ui->color_methodEdit->addItem("Mosher");
    ui->color_methodEdit->addItem("Daniels");
    ui->color_methodEdit->addItem("Halberstadt");
    ui->color_methodEdit->addItem("Naudts");

    ui->ibu_methodEdit->addItem("Tinseth");
    ui->ibu_methodEdit->addItem("Rager");
    ui->ibu_methodEdit->addItem("Daniels");

    ui->beerstyleEdit->addItem("");	// First add a dummy
    query.prepare("SELECT style_guide,style_letter,name FROM profile_styles ORDER BY style_guide,style_letter,name");
    query.exec();
    query.first();
    for (int i = 0; i < query.size(); i++) {
	ui->beerstyleEdit->addItem(query.value(0).toString()+" "+query.value(1).toString()+" "+query.value(2).toString());
	query.next();
    }

    if (id >= 0) {
	query.prepare("SELECT * FROM recipes WHERE record = :recno");
	query.bindValue(":recno", id);
	query.exec();
	query.next();

	ui->lockedEdit->setChecked(query.value(2).toInt() ? true:false);
	ui->st_nameEdit->setText(query.value(3).toString());
	ui->st_groupEdit->setText(query.value(4).toString());
	ui->st_guideEdit->setText(query.value(5).toString());
	ui->st_catEdit->setText(query.value(6).toString());
	ui->st_catnrEdit->setText(query.value(7).toString());
	ui->st_typeEdit->setText(s_types[query.value(8).toInt()]);

	ui->nameEdit->setText(query.value(21).toString());
	ui->notesEdit->setPlainText(query.value(22).toString());
	ui->typeEdit->setCurrentIndex(query.value(23).toInt());
	ui->batch_sizeEdit->setValue(query.value(24).toDouble());
	ui->boil_sizeEdit->setValue(query.value(25).toDouble());
	ui->boil_timeEdit->setValue(query.value(26).toInt());
	ui->efficiencyEdit->setValue(query.value(27).toDouble());

	ui->est_ogEdit->setValue(query.value(28).toDouble());
	ui->est_og2Edit->setValue(query.value(28).toDouble());
	ui->est_og3Edit->setValue(query.value(28).toDouble());
	ui->est_ogShow->setRange(query.value(9).toDouble(), query.value(10).toDouble());
	ui->est_ogShow->setPrecision(3);
	ui->est_ogShow->setMarkerTextIsValue(true);
	ui->est_ogShow->setValue(query.value(28).toDouble());

	ui->est_fgEdit->setValue(query.value(29).toDouble());
	ui->est_fg3Edit->setValue(query.value(29).toDouble());
        ui->est_fgShow->setRange(query.value(11).toDouble(), query.value(12).toDouble());
        ui->est_fgShow->setPrecision(3);
        ui->est_fgShow->setMarkerTextIsValue(true);
        ui->est_fgShow->setValue(query.value(29).toDouble());

	ui->est_abvEdit->setValue(query.value(30).toDouble());
	ui->est_abv2Edit->setValue(query.value(30).toDouble());
        ui->est_abvShow->setRange(query.value(19).toDouble(), query.value(20).toDouble());
        ui->est_abvShow->setPrecision(1);
        ui->est_abvShow->setMarkerTextIsValue(true);
        ui->est_abvShow->setValue(query.value(30).toDouble());

	//QColor color = Utils::ebc_to_color(query.value(31).toInt());
	ui->est_colorEdit->setValue(query.value(31).toDouble());
	ui->est_colorEdit->setStyleSheet(Utils::ebc_to_style(query.value(31).toInt()));
	ui->est_color2Edit->setValue(query.value(31).toDouble());
        ui->est_color2Edit->setStyleSheet(Utils::ebc_to_style(query.value(31).toInt()));
        ui->est_colorShow->setPrecision(0);
        ui->est_colorShow->setMarkerTextIsValue(true);
	ui->est_colorShow->setRange(query.value(15).toDouble(), query.value(16).toDouble());
        ui->est_colorShow->setValue(query.value(31).toDouble());

	ui->color_methodEdit->setCurrentIndex(query.value(32).toInt());

	ui->est_ibuEdit->setValue(query.value(33).toDouble());
	ui->est_ibu2Edit->setValue(query.value(33).toDouble());
        ui->est_ibuShow->setPrecision(0);
        ui->est_ibuShow->setMarkerTextIsValue(true);
        ui->est_ibuShow->setRange(query.value(13).toDouble(), query.value(14).toDouble());
        ui->est_ibuShow->setValue(query.value(33).toDouble());

	ui->ibu_methodEdit->setCurrentIndex(query.value(34).toInt());

	ui->est_carbEdit->setValue(query.value(35).toDouble());
        ui->est_carbShow->setPrecision(1);
        ui->est_carbShow->setMarkerTextIsValue(true);
        ui->est_carbShow->setRange(query.value(17).toDouble(), query.value(18).toDouble());
        ui->est_carbShow->setValue(query.value(35).toDouble());

	// 36 sparge_temp
	// 37 sparge_ph
	// 38 sparge_volume
	// 39 sparge_source
	// 40 sparge_acid_type
	// 41 sparge_acid_perc
	// 42 sparge_acid_amount
	// 43 mash_ph
	// 44 mash_name
	// 45 calc_acid
	// 46 w1_name
	// 47 w1_amount
	// 48 w1_calcium
	// 49 w1_sulfate
	// 50 w1_chloride
	// 51 w1_sodium
	// 52 w1_magnesium
	// 53 w1_total_alkalinity
	// 54 w1_ph
	// 55 w1_cost
	// 56 w2_name
	// 57 w2_amount
	// 58 w2_calcium
	// 59 w2_sulfate
	// 60 w2_chloride
	// 61 w2_sodium
	// 62 w2_magnesium
	// 63 w2_total_alkalinity
	// 64 w2_ph
	// 65 w2_cost
	// 66 wg_amount
	// 67 wg_calcium
	// 68 wg_sulfate
	// 69 wg_chloride
	// 70 wg_sodium
	// 71 wg_magnesium
	// 72 wg_total_alkalinity
	// 73 wg_ph
	// 74 wb_calcium
	// 75 wb_sulfate
	// 76 wb_chloride
	// 77 wb_sodium
	// 78 wb_magnesium
	// 79 wb_total_alkalinity
	// 80 wb_ph
	// 81 wa_acid_name
	// 82 wa_acid_perc
	// 83 wa_base_name


	QJsonParseError parseError;
        const auto& f_json = query.value(84).toString();
	if (!f_json.trimmed().isEmpty()) {
            const auto& formattedJson = QString("%1").arg(f_json);
            this->fermentables = QJsonDocument::fromJson(formattedJson.toUtf8(),  &parseError);
            if (parseError.error != QJsonParseError::NoError)
                qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ;
        } else {
	    qDebug() << "empty fermentables";
	}

	const auto& h_json = query.value(85).toString();
        if (!h_json.trimmed().isEmpty()) {
            const auto& formattedJson = QString("%1").arg(h_json);
            this->hops = QJsonDocument::fromJson(formattedJson.toUtf8(),  &parseError);
            if (parseError.error != QJsonParseError::NoError)
                qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ;
        } else {
            qDebug() << "empty hops";
        }

	const auto& m_json = query.value(86).toString();
        if (!m_json.trimmed().isEmpty()) {
            const auto& formattedJson = QString("%1").arg(m_json);
            this->miscs = QJsonDocument::fromJson(formattedJson.toUtf8(),  &parseError);
            if (parseError.error != QJsonParseError::NoError)
                qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ;
        } else {
            qDebug() << "empty miscs";
        }

	const auto& y_json = query.value(87).toString();
        if (!y_json.trimmed().isEmpty()) {
            const auto& formattedJson = QString("%1").arg(y_json);
            this->yeasts = QJsonDocument::fromJson(formattedJson.toUtf8(),  &parseError);
            if (parseError.error != QJsonParseError::NoError)
                qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ;
        } else {
            qDebug() << "empty yeasts";
        }

	const auto& ma_json = query.value(88).toString();
        if (!ma_json.trimmed().isEmpty()) {
            const auto& formattedJson = QString("%1").arg(ma_json);
            this->mashs = QJsonDocument::fromJson(formattedJson.toUtf8(),  &parseError);
            if (parseError.error != QJsonParseError::NoError)
                qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ;
        } else {
            qDebug() << "empty mashs";
        }
    } else {
	/* Set some defaults */

	const auto& formattedJson = QString("[]");
	this->fermentables = QJsonDocument::fromJson(formattedJson.toUtf8());
	this->hops = QJsonDocument::fromJson(formattedJson.toUtf8());
	this->miscs = QJsonDocument::fromJson(formattedJson.toUtf8());
	this->yeasts = QJsonDocument::fromJson(formattedJson.toUtf8());
	this->mashs = QJsonDocument::fromJson(formattedJson.toUtf8());
    }

    // All signals from tab "Generic"
    connect(ui->lockedEdit, &QCheckBox::stateChanged, this, &EditRecipe::is_changed);
    connect(ui->nameEdit, &QLineEdit::textChanged, this, &EditRecipe::is_changed);
    connect(ui->notesEdit, SIGNAL(textChanged()), this, SLOT(is_changed()));
    connect(ui->typeEdit, &QComboBox::currentTextChanged, this, &EditRecipe::is_changed);
    connect(ui->batch_sizeEdit, &QDoubleSpinBox::textChanged, this, &EditRecipe::is_changed);
    connect(ui->boil_timeEdit, &QSpinBox::textChanged, this, &EditRecipe::is_changed);
    connect(ui->efficiencyEdit, &QDoubleSpinBox::textChanged, this, &EditRecipe::is_changed);
    connect(ui->beerstyleEdit, &QComboBox::currentTextChanged, this, &EditRecipe::style_changed);
    connect(ui->est_ogEdit, &QDoubleSpinBox::textChanged, this, &EditRecipe::is_changed);
    connect(ui->color_methodEdit, &QComboBox::currentTextChanged, this, &EditRecipe::colormethod_changed);
    connect(ui->ibu_methodEdit, &QComboBox::currentTextChanged, this, &EditRecipe::is_changed);

    // All signals from tab "Fermentables"
    connect(ui->est_og2Edit, &QDoubleSpinBox::textChanged, this, &EditRecipe::is_changed);
    connect(ui->perc_mashShow, &QProgressBar::valueChanged, this, &EditRecipe::on_perc_mash_valueChanged);
    connect(ui->perc_sugarsShow, &QProgressBar::valueChanged, this, &EditRecipe::on_perc_sugars_valueChanged);
    connect(ui->perc_caraShow, &QProgressBar::valueChanged, this, &EditRecipe::on_perc_cara_valueChanged);
    connect(ui->lintnerShow, &QProgressBar::valueChanged, this, &EditRecipe::on_lintner_valueChanged);
    connect(ui->fermentablesTable, SIGNAL(cellChanged(int, int)), this, SLOT(cell_Fermentable_changed(int, int)));

    // All signals from tab "Hops"
//    connect(ui->hopsTable, SIGNAL(cellChanged(int, int)), this, SLOT(cell_Changed(int, int)));

    // All signals from tab "Miscs"
//    connect(ui->hopsTable, SIGNAL(cellChanged(int, int)), this, SLOT(cell_Changed(int, int)));

    // All signals from tab "Yeasts"
//    connect(ui->hopsTable, SIGNAL(cellChanged(int, int)), this, SLOT(cell_Changed(int, int)));

    // All signals from tab "Mash"
//    connect(ui->hopsTable, SIGNAL(cellChanged(int, int)), this, SLOT(cell_Changed(int, int)));

    // All signals from tab "Water"

    ui->saveButton->setEnabled(false);
    ui->deleteButton->setEnabled((id >= 0) ? true:false);

    emit refreshAll();
}


EditRecipe::~EditRecipe()
{
    qDebug() << "EditRecipe done start";
    delete ui;
    emit entry_changed();
    qDebug() << "EditRecipe done final";
}


void EditRecipe::refreshFermentables()
{
    QString w;
    QWidget* pWidget;
    QHBoxLayout* pLayout;
    QTableWidgetItem *item;
    QLabel *label;
    double  d;
    int j;
    int total = 0;

    qDebug() << "refreshFermentables" << this->fermentables << this->fermentables.isArray() << this->fermentables.array().size();
    /* During filling the table turn off the cellChanged signal because every cell that is filled
     * triggers the cellChanged signal. The QTableWidget has no better signal to use. */
    this->ignoreChanges = true;

    const QStringList labels({tr("Fermentable"), tr("EBC"), tr("Type"), tr("Graintype"), tr("When"), tr("Yield"), tr("Amount"), tr("Procent"), tr("100%"), tr("Button")});
    ui->fermentablesTable->setColumnCount(10);
    ui->fermentablesTable->setColumnWidth(0, 350);     /* Fermentable	*/
    ui->fermentablesTable->setColumnWidth(1,  50);     /* Color		*/
    ui->fermentablesTable->setColumnWidth(2,  75);     /* Type		*/
    ui->fermentablesTable->setColumnWidth(3,  75);     /* Graintype	*/
    ui->fermentablesTable->setColumnWidth(4,  75);     /* Added		*/
    ui->fermentablesTable->setColumnWidth(5,  75);     /* Yield		*/
    ui->fermentablesTable->setColumnWidth(6,  75);     /* Amount	*/
    ui->fermentablesTable->setColumnWidth(7,  60);     /* Procent	*/
    ui->fermentablesTable->setColumnWidth(8,  50);     /* 100%		*/
    ui->fermentablesTable->setColumnWidth(9,  80);     /* Button	*/
    ui->fermentablesTable->setHorizontalHeaderLabels(labels);
    ui->fermentablesTable->verticalHeader()->hide();
    ui->fermentablesTable->setRowCount(this->fermentables.array().size());

    if (this->fermentables.isArray()) {
	for (int i = 0; i < this->fermentables.array().size(); i++) {
            QJsonObject obj = this->fermentables.array().at(i).toObject();

	    w = obj["f_supplier"].toString()+" / "+obj["f_name"].toString();
            ui->fermentablesTable->setItem(i, 0, new QTableWidgetItem(w));

	    if (obj["f_color"].isString())
                d = QString(obj["f_color"].toString()).toDouble();
            else
                d = obj["f_color"].toDouble();
            w = QString("%1").arg(d, 1, 'f', 0, '0');
            item = new QTableWidgetItem(w);
            item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
            ui->fermentablesTable->setItem(i, 1, item);

	    if (obj["f_type"].isString())
                j = QString(obj["f_type"].toString()).toInt();
            else
                j = obj["f_type"].toInt();
	    item = new QTableWidgetItem(f_types[j]);
	    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
	    ui->fermentablesTable->setItem(i, 2, item);

	    if (obj["f_graintype"].isString())
                j = QString(obj["f_graintype"].toString()).toInt();
            else
                j = obj["f_graintype"].toInt();
            item = new QTableWidgetItem(f_graintypes[j]);
            item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
            ui->fermentablesTable->setItem(i, 3, item);

	    if (obj["f_added"].isString())
                j = QString(obj["f_added"].toString()).toInt();
            else
                j = obj["f_added"].toInt();
            item = new QTableWidgetItem(f_added[j]);
            item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
            ui->fermentablesTable->setItem(i, 4, item);

	    if (obj["f_yield"].isString())
                d = QString(obj["f_yield"].toString()).toDouble();
            else
                d = obj["f_yield"].toDouble();
            w = QString("%1%").arg(d, 2, 'f', 1, '0');
            item = new QTableWidgetItem(w);
            item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
            ui->fermentablesTable->setItem(i, 5, item);

	    if (obj["f_amount"].isString())
                d = QString(obj["f_amount"].toString()).toDouble();
            else
                d = obj["f_amount"].toDouble();
            w = QString("%1 Kg").arg(d, 4, 'f', 3, '0');
            item = new QTableWidgetItem(w);
            item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
            ui->fermentablesTable->setItem(i, 6, item);

	    if (obj["f_percentage"].isString())
                d = QString(obj["f_percentage"].toString()).toDouble();
            else
                d = obj["f_percentage"].toDouble();
            w = QString("%1%").arg(d, 2, 'f', 1, '0');
            item = new QTableWidgetItem(w);
            item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
            ui->fermentablesTable->setItem(i, 7, item);

	    QTableWidgetItem *checkBoxItem = new QTableWidgetItem();
	    checkBoxItem->setCheckState((obj["f_adjust_to_total_100"].toInt()) ? Qt::Checked : Qt::Unchecked);
	    checkBoxItem->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
            ui->fermentablesTable->setItem(i, 8, checkBoxItem);

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

    this->ignoreChanges = false;
}


void EditRecipe::refreshHops()
{
}


void EditRecipe::refreshMiscs()
{
}


void EditRecipe::refreshYeasts()
{
}


void EditRecipe::refreshMashs()
{
}


void EditRecipe::refreshAll()
{
    refreshFermentables();

    calcFermentables();
}


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

    qDebug() << "calcFermentables()";

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

    if (this->fermentables.array().size() < 1) {
	qDebug() << "  no fermentables, return.";
	return;
    }

    for (i = 0; i < this->fermentables.array().size(); i++) {
	obj = this->fermentables.array().at(i).toObject();
	if (obj["f_adjust_to_total_100"].toInt()) {
	    my_100 = true;
	}
	if (obj["f_type"].toInt() == 1 && obj["f_added"].toInt() < 4)		// Sugars
	    psugar += obj["f_percentage"].toDouble();
	if (obj["f_graintype"].toInt() == 2 && obj["f_added"].toInt() < 4)	// Crystal/Cara
	    pcara += obj["f_percentage"].toDouble();
	d = obj["f_amount"].toDouble() * (obj["f_yield"].toDouble() / 100) * (1 - obj["f_moisture"].toDouble() / 100);
	if (obj["f_added"].toInt() == 0) {					// Mash
	    if (mvol > 0) {							// If mash volume is known
		mvol += obj["f_amount"].toDouble() * obj["f_moisture"].toDouble() / 100;
		s += d;
	    }
	    d = ui->efficiencyEdit->value() / 100 * d;
	    sugarsm += d;
	    mashkg += obj["f_amount"].toDouble();
	}
	if (obj["f_added"].toInt() == 0 || obj["f_added"].toInt() == 1)		// Mash or boil
	    sugarsf += d;
	if (obj["f_added"].toInt() == 2 || obj["f_added"].toInt() == 3) {		// Fermentation or lagering
	    x = (obj["f_yield"].toDouble() / 100) * (1 - obj["f_moisture"].toDouble() / 100);
	    addedS += obj["f_amount"].toDouble() * x;
	    addedmass += obj["f_amount"].toDouble();
	    vol += (x * sugardensity + (1 - x) * 1) * obj["f_amount"].toDouble();
	}
	if (obj["f_added"].toInt() == 0 && (obj["f_type"].toInt() == 0 || obj["f_type"].toInt() == 4) && obj["f_color"].toDouble() < 50) {
	    lintner += obj["f_diastatic_power"].toDouble() * obj["f_amount"].toDouble();
	}
	if (obj["f_added"].toInt() < 4) {
	    colort += obj["f_amount"].toDouble() * Utils::ebc_to_srm(obj["f_color"].toDouble());
	    colorh += obj["f_amount"].toDouble() * obj["f_color"].toDouble() * Utils::get_kt(obj["f_color"].toDouble());
	    colorn += (obj["f_percentage"].toDouble() / 100) * obj["f_color"].toDouble();	// For 8.6 Pt wort.
	}
    }
    qDebug() << "  colort" << colort << "colorh" << colorh << "colorn" << colorn;
    qDebug() << "  psugar" << psugar << "pcara" << pcara << "mvol" << mvol;
    qDebug() << "  sugarsf" << sugarsf << "sugarsm" << sugarsm;

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

    double preboil_sg = Utils::estimate_sg(sugarsm, ui->boil_sizeEdit->value());
    qDebug() << "  preboil SG" << preboil_sg;

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

    /*
     * We don't have a equipment profile in recipes,
     * so we assume a certain guessed mashtun size.
     */
    ui->perc_mashShow->setValue(round(mashkg / (ui->boil_sizeEdit->value() / 3) * 100));
    ui->perc_sugarsShow->setValue(round(psugar));
    ui->perc_caraShow->setValue(round(pcara));
    qDebug() << "  lintner" << lintner << "  mashkg" << mashkg << "final" << round(lintner / mashkg);
    ui->lintnerShow->setValue(round(lintner / mashkg));

    /*
     * Calculate the apparant attenuation.
     */
    double svg = 0;
    if (this->yeasts.array().size() > 0) {
        for (i = 0; i < this->yeasts.array().size(); i++) {
            obj = this->yeasts.array().at(i).toObject();
	    if (obj["y_use"].toInt() == 0) {			// Used in primary
		if (obj["y_attenuation"].toDouble() > svg)
		    svg = obj["y_attenuation"].toDouble();	// Take the highest if multiple yeasts.
	    }
	    // TODO: brett or others in secondary.
	}
	qDebug() << "  SVG" << svg;
    }
    if (svg == 0)
	svg = 77.0;

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

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

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

    // If to_100 then make all amount fields t/o and percent fields r/w

}


void EditRecipe::on_perc_mash_valueChanged(int value)
{
    if (value < 90)
	ui->perc_mashShow->setStyleSheet(bar_green);
    else if (value < 100)
	ui->perc_mashShow->setStyleSheet(bar_orange);
    else
	ui->perc_mashShow->setStyleSheet(bar_red);
}


void EditRecipe::on_perc_sugars_valueChanged(int value)
{
    if (value < 20)
	ui->perc_sugarsShow->setStyleSheet(bar_green);
    else
	ui->perc_sugarsShow->setStyleSheet(bar_red);
}


void EditRecipe::on_perc_cara_valueChanged(int value)
{
    if (value < 25)
	ui->perc_caraShow->setStyleSheet(bar_green);
    else
	ui->perc_caraShow->setStyleSheet(bar_red);
}


void EditRecipe::on_lintner_valueChanged(int value)
{
    if (value < 30)
	ui->lintnerShow->setStyleSheet(bar_red);
    else if (value < 40)
	ui->lintnerShow->setStyleSheet(bar_orange);
    else
	ui->lintnerShow->setStyleSheet(bar_green);
}


/*
 * Window header, mark any change with '**'
 */
void EditRecipe::WindowTitle()
{
    QString txt;

    if (this->recno < 0) {
	txt = QString(tr("BMSapp - Add new recipe"));
    } else {
	txt = QString(tr("BMSapp - Edit recipe %1").arg(this->recno));
    }

    if (this->textIsChanged) {
	txt.append((QString(" **")));
    }
    setWindowTitle(txt);
}


void EditRecipe::on_saveButton_clicked()
{
    QSqlQuery query;

    /* If there are errors in the form, show a message and do "return;" */
    if (ui->nameEdit->text().length() < 2) {
	QMessageBox::warning(this, tr("Edit Recipe"), tr("Name empty or too short."));
	return;
    }

    if (this->textIsChanged) {
    	if (this->recno == -1) {
    	    query.prepare("INSERT INTO recipes SET name=:name, "
		"uuid = :uuid");
    	} else {
	    query.prepare("UPDATE recipes SET name=:name "
                " WHERE record = :recno");
    	}
	//query.bindValue(":name", ui->nameEdit->text());
	//query.bindValue(":notes", ui->notesEdit->toPlainText());
	if (this->recno == -1) {
	    query.bindValue(":uuid", QUuid::createUuid().toString().mid(1, 36));
	} else {
	    query.bindValue(":recno", this->recno);
	}
	query.exec();
	if (query.lastError().isValid()) {
	    qDebug() << "EditRecipe" << query.lastError();
	    QMessageBox::warning(this, tr("Database error"),
                        tr("MySQL error: %1\n%2\n%3")
                        .arg(query.lastError().nativeErrorCode())
                        .arg(query.lastError().driverText())
                        .arg(query.lastError().databaseText()));
	} else {
	    qDebug() << "EditRecipe Saved";
	}
    }

    ui->saveButton->setEnabled(false);
    this->textIsChanged = false;
    WindowTitle();
}


void EditRecipe::on_deleteButton_clicked()
{
    QSqlQuery query;

    query.prepare("DELETE FROM recipes WHERE record = :recno");
    query.bindValue(":recno", this->recno);
    query.exec();
    if (query.lastError().isValid()) {
	qDebug() << "EditRecipe" << query.lastError();
	QMessageBox::warning(this, tr("Database error"),
                        tr("MySQL error: %1\n%2\n%3")
                        .arg(query.lastError().nativeErrorCode())
                        .arg(query.lastError().driverText())
                        .arg(query.lastError().databaseText()));
    } else {
	qDebug() << "EditRecipe Deleted" << this->recno;
    }

    this->close();
    this->setResult(1);
}


void EditRecipe::is_changed()
{
    ui->saveButton->setEnabled(true);
    ui->deleteButton->setEnabled((this->recno >= 0) ? true:false);
    this->textIsChanged = true;
    WindowTitle();
}


/*
 * New beerstyle is selected.
 */
void EditRecipe::style_changed()
{
    QSqlQuery query;

    if (ui->beerstyleEdit->currentIndex() < 1)
	return;

    query.prepare("SELECT * FROM profile_styles ORDER BY style_guide,style_letter,name");
    query.exec();
    query.first();
    // Skip to the record index.
    for (int i = 0; i < (ui->beerstyleEdit->currentIndex() - 1); i++) {
        query.next();
    }
    // Set relevant fields and update ranges.
    ui->st_nameEdit->setText(query.value(1).toString());
    ui->st_catEdit->setText(query.value(2).toString());
    ui->st_catnrEdit->setText(query.value(3).toString());
    ui->st_groupEdit->setText(query.value(4).toString());
    ui->st_guideEdit->setText(query.value(5).toString());
    ui->st_typeEdit->setText(s_types[query.value(6).toInt()]);
    ui->est_ogShow->setRange(query.value(7).toDouble(), query.value(8).toDouble());
    ui->est_fgShow->setRange(query.value(9).toDouble(), query.value(10).toDouble());
    ui->est_ibuShow->setRange(query.value(11).toDouble(), query.value(12).toDouble());
    ui->est_colorShow->setRange(query.value(13).toDouble(), query.value(14).toDouble());
    ui->est_carbShow->setRange(query.value(15).toDouble(), query.value(16).toDouble());
    ui->est_abvShow->setRange(query.value(17).toDouble(), query.value(18).toDouble());

    is_changed();
}


void EditRecipe::colormethod_changed()
{
    calcFermentables();
    is_changed();
}


void EditRecipe::time_changed()
{
    is_changed();
}


void EditRecipe::cell_Fermentable_changed(int nRow, int nCol)
{
    if (this->ignoreChanges)
        return;

    qDebug() << "Cell at row " + QString::number(nRow) + " column " + QString::number(nCol) + " was changed.";

    if (nCol == 8) {	// 100% checkbox
	this->ignoreChanges = true;

	if (ui->fermentablesTable->item(nRow, nCol)->checkState() == Qt::Checked) {
	    /*
	     * This row is checked. Remove any other checked item.
	     */
	    for (int i = 0; i < ui->fermentablesTable->rowCount(); i++) {
		if (i != nRow) {
		    QTableWidgetItem *checkBoxItem = ui->fermentablesTable->item(i, nCol);
		    checkBoxItem->setCheckState(Qt::Unchecked);
		    ui->fermentablesTable->setItem(i, nCol, checkBoxItem);
		}
	    }
	} else {
	    /*
	     * Unchecked, start working with amounts instead of percentages.
	     */
	}
	qDebug() << ui->fermentablesTable->item(nRow, nCol)->checkState();
	this->ignoreChanges = false;
    }

    // TODO: some checks and auto fixes.
//    make_Json();
}


void EditRecipe::on_deleteFermentRow_clicked()
{
    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
    int row = pb->objectName().toInt();
    qDebug() << "Delete row" << row;
    ui->fermentablesTable->removeRow(row);
//    make_Json();
}


void EditRecipe::on_quitButton_clicked()
{
    if (this->textIsChanged) {
	int rc = QMessageBox::warning(this, tr("Recipe changed"), tr("The recipe has been modified. Save changes?"),
                                QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Save);
        switch (rc) {
            case QMessageBox::Save:
                        on_saveButton_clicked();
                        break;  /* Saved and then Quit */
            case QMessageBox::Discard:
                        break;  /* Quit without Save */
            case QMessageBox::Cancel:
                        return; /* Return to the editor page */
        }
    }

    this->close();
    this->setResult(1);
}

mercurial