src/EditRecipe.cpp

Wed, 06 Apr 2022 20:26:47 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Wed, 06 Apr 2022 20:26:47 +0200
changeset 110
224be4d9f8eb
parent 109
1ce50e72a6b1
child 111
04f5a7c5a1dc
permissions
-rw-r--r--

Fermentables are loaded in the main recipe record in a QList. The refresh table function does the sorting, on added moment (mash first), amount and finally color of the malt. Removed the old hidden table columns. Do all manipulation on the recipe record with QList arrays. The ferment_amount_changed() slot is complete. The whole recipe editor is going in the right direction.

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



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

    qDebug() << "EditRecipe record:" << id;
    recipe = new Recipe;
    ui->setupUi(this);
    recipe->fermentables_current = recipe->hops_current = recipe->misc_current = recipe->yeasts_current = recipe->mashs_current = -1;
    recipe->fermentables_use100 = false;
    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();

	recipe->record = query.value(0).toInt();
	recipe->uuid = query.value(1).toString();
	recipe->locked = query.value(2).toInt() ? true:false;
	recipe->st_name = query.value(3).toString();
	recipe->st_letter = query.value(4).toString();
	recipe->st_guide = query.value(5).toString();
	recipe->st_category = query.value(6).toString();
	recipe->st_category_number = query.value(7).toInt();
	recipe->st_type = query.value(8).toInt();
	recipe->st_og_min = query.value(9).toDouble();
	recipe->st_og_max = query.value(10).toDouble();
	recipe->st_fg_min = query.value(11).toDouble();
	recipe->st_fg_max = query.value(12).toDouble();
	recipe->st_ibu_min = query.value(13).toDouble();
	recipe->st_ibu_max = query.value(14).toDouble();
	recipe->st_color_min = query.value(15).toDouble();
	recipe->st_color_max = query.value(16).toDouble();
	recipe->st_carb_min = query.value(17).toDouble();
	recipe->st_carb_max = query.value(18).toDouble();
	recipe->st_abv_min = query.value(19).toDouble();
	recipe->st_abv_max = query.value(20).toDouble();
	recipe->name = query.value(21).toString();
	recipe->notes = query.value(22).toString();
	recipe->type = query.value(23).toInt();
	recipe->batch_size = query.value(24).toDouble();
	recipe->boil_size = query.value(25).toDouble();
	recipe->boil_time = query.value(26).toDouble();
	recipe->efficiency = query.value(27).toDouble();
	recipe->est_og = query.value(28).toDouble();
	recipe->est_fg = query.value(29).toDouble();
	recipe->est_abv = query.value(30).toDouble();
	recipe->est_color = query.value(31).toDouble();
	recipe->color_method = query.value(32).toInt();
	recipe->est_ibu = query.value(33).toDouble();
	recipe->ibu_method = query.value(34).toInt();
	recipe->est_carb = query.value(35).toDouble();

	recipe->sparge_temp = query.value(36).toDouble();
	recipe->sparge_ph = query.value(37).toDouble();
	recipe->sparge_volume = query.value(38).toDouble();
	recipe->sparge_source = query.value(39).toInt();
	recipe->sparge_acid_type = query.value(40).toInt();
	recipe->sparge_acid_perc = query.value(41).toDouble();
	recipe->sparge_acid_amount = query.value(42).toDouble();
	recipe->mash_ph = query.value(43).toDouble();
	recipe->mash_name = query.value(44).toDouble();
	recipe->calc_acid = query.value(45).toInt() ? true:false;

	recipe->w1_name = query.value(46).toString();
	recipe->w1_amount = query.value(47).toDouble();
	recipe->w1_calcium = query.value(48).toDouble();
	recipe->w1_sulfate = query.value(49).toDouble();
	recipe->w1_chloride = query.value(50).toDouble();
	recipe->w1_sodium = query.value(51).toDouble();
	recipe->w1_magnesium = query.value(52).toDouble();
	recipe->w1_total_alkalinity = query.value(53).toDouble();
	recipe->w1_ph = query.value(54).toDouble();
	recipe->w1_cost = query.value(55).toDouble();
	recipe->w2_name = query.value(56).toString();
        recipe->w2_amount = query.value(57).toDouble();
        recipe->w2_calcium = query.value(58).toDouble();
        recipe->w2_sulfate = query.value(59).toDouble();
        recipe->w2_chloride = query.value(60).toDouble();
        recipe->w2_sodium = query.value(61).toDouble();
        recipe->w2_magnesium = query.value(62).toDouble();
        recipe->w2_total_alkalinity = query.value(63).toDouble();
        recipe->w2_ph = query.value(64).toDouble();
        recipe->w2_cost = query.value(65).toDouble();
	recipe->wg_amount = query.value(66).toDouble();
        recipe->wg_calcium = query.value(67).toDouble();
        recipe->wg_sulfate = query.value(68).toDouble();
        recipe->wg_chloride = query.value(69).toDouble();
        recipe->wg_sodium = query.value(70).toDouble();
        recipe->wg_magnesium = query.value(71).toDouble();
        recipe->wg_total_alkalinity = query.value(72).toDouble();
        recipe->wg_ph = query.value(73).toDouble();
	recipe->wb_calcium = query.value(74).toDouble();
        recipe->wb_sulfate = query.value(75).toDouble();
        recipe->wb_chloride = query.value(76).toDouble();
        recipe->wb_sodium = query.value(77).toDouble();
        recipe->wb_magnesium = query.value(78).toDouble();
        recipe->wb_total_alkalinity = query.value(79).toDouble();
        recipe->wb_ph = query.value(80).toDouble();
	recipe->wa_acid_name = query.value(81).toInt();
        recipe->wa_acid_perc = query.value(82).toDouble();
        recipe->wa_base_name = query.value(83).toInt();

	QJsonParseError parseError;
        const auto& f_json = query.value(84).toString();
	if (!f_json.trimmed().isEmpty()) {
            const auto& formattedJson = QString("%1").arg(f_json);
            QJsonDocument fermentables = QJsonDocument::fromJson(formattedJson.toUtf8(),  &parseError);
            if (parseError.error != QJsonParseError::NoError) {
                qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ;
	    } else if (fermentables.isArray()) {
		for (int i = 0; i < fermentables.array().size(); i++) {
		    QJsonObject obj = fermentables.array().at(i).toObject();
		    Fermentables f;
		    f.f_name = obj["f_name"].toString();
		    f.f_origin = obj["f_origin"].toString();
		    f.f_supplier = obj["f_supplier"].toString();
		    f.f_amount = obj["f_amount"].toDouble();
		    f.f_cost = obj["f_cost"].toDouble();
		    f.f_type = obj["f_type"].toInt();
		    f.f_yield = obj["f_yield"].toDouble();
		    f.f_color = obj["f_color"].toDouble();
		    f.f_coarse_fine_diff = obj["f_coarse_fine_diff"].toDouble();
		    f.f_moisture = obj["f_moisture"].toDouble();
		    f.f_diastatic_power = obj["f_diastatic_power"].toDouble();
		    f.f_protein = obj["f_protein"].toDouble();
		    f.f_dissolved_protein = obj["f_dissolved_protein"].toDouble();
		    f.f_max_in_batch = obj["f_max_in_batch"].toDouble();
		    f.f_graintype = obj["f_graintype"].toInt();
		    f.f_added = obj["f_added"].toInt();
		    f.f_recommend_mash = obj["f_recommend_mash"].toInt() ? true:false;
		    f.f_add_after_boil = obj["f_add_after_boil"].toInt() ? true:false;
		    f.f_adjust_to_total_100 = obj["f_adjust_to_total_100"].toInt() ? true:false;
		    f.f_percentage = obj["f_percentage"].toDouble();
		    f.f_di_ph = obj["f_di_ph"].toDouble();
		    f.f_acid_to_ph_57 = obj["f_acid_to_ph_57"].toDouble();
		    if (f.f_adjust_to_total_100)
			recipe->fermentables_use100 = true;
		    recipe->fermentables.append(f);
		}
		qDebug() << "fermentables" << recipe->fermentables.size();
	    }
        } 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);
            QJsonDocument mashs = QJsonDocument::fromJson(formattedJson.toUtf8(),  &parseError);
            if (parseError.error != QJsonParseError::NoError) {
                qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ;
	    } else if (mashs.isArray()) {
        	for (int i = 0; i < mashs.array().size(); i++) {
            	    QJsonObject obj = mashs.array().at(i).toObject();
//		    qDebug() << i << obj;
		    Mashs m;
		    m.step_name = obj["step_name"].toString();
		    m.step_type = obj["step_type"].toInt();
		    m.step_volume = obj["step_volume"].toDouble();
		    m.step_infuse_amount = obj["step_infuse_amount"].toDouble();
		    m.step_infuse_temp = obj["step_infuse_temp"].toDouble();
		    m.step_temp = obj["step_temp"].toDouble();
		    m.step_time = obj["step_time"].toDouble();
		    m.ramp_time = obj["ramp_time"].toDouble();
		    m.end_temp = obj["end_temp"].toDouble();
		    m.step_wg_ratio = obj["step_wg_ratio"].toDouble();
		    recipe->mashs.append(m);
		}
	    }
        } else {
            qDebug() << "empty mashs";
        }
	qDebug() << "mashs" << recipe->mashs.size();

    } else {
	/* Set some defaults */
	recipe->locked = false;
	recipe->st_name = "";
	recipe->st_letter = "";
	recipe->st_guide = "";
	recipe->st_category = "";
	recipe->st_category_number = 0;
	recipe->st_type = 0;
	recipe->st_og_min = 1.025; recipe->st_og_max = 1.100;
	recipe->st_fg_min = 1.000; recipe->st_fg_max = 1.020;
	recipe->st_ibu_min = 5; recipe->st_ibu_max = 200;
	recipe->st_color_min = 3; recipe->st_color_max = 100;
	recipe->st_carb_min = 1.0; recipe->st_carb_max = 4.5;
	recipe->st_abv_min = 1; recipe->st_abv_max = 15;
	recipe->efficiency = 75;
	recipe->batch_size = 20;
	recipe->boil_size = 24;
	recipe->boil_time = 60;
	recipe->type = 2;
	recipe->sparge_temp = 80;
        recipe->sparge_ph = 5.4;
        recipe->sparge_volume = 8;
        recipe->sparge_source = 0;
        recipe->sparge_acid_type = 0;
        recipe->sparge_acid_perc = 80;
        recipe->sparge_acid_amount = 0;
        recipe->mash_ph = 5.4;
        recipe->mash_name = "";
        recipe->calc_acid = true;
	recipe->w1_name = "";
        recipe->w1_amount = 0;
        recipe->w1_calcium = 0;
        recipe->w1_sulfate = 0;
        recipe->w1_chloride = 0;
        recipe->w1_sodium = 0;
        recipe->w1_magnesium = 0;
        recipe->w1_total_alkalinity = 0;
        recipe->w1_ph = 7;
        recipe->w1_cost = 0;
        recipe->w2_name = "";
        recipe->w2_amount = 0;
        recipe->w2_calcium = 0;
        recipe->w2_sulfate = 0;
        recipe->w2_chloride = 0;
        recipe->w2_sodium = 0;
        recipe->w2_magnesium = 0;
        recipe->w2_total_alkalinity = 0;
        recipe->w2_ph = 7;
        recipe->w2_cost = 0;
        recipe->wg_amount = 0;
        recipe->wg_calcium = 0;
        recipe->wg_sulfate = 0;
        recipe->wg_chloride = 0;
        recipe->wg_sodium = 0;
        recipe->wg_magnesium = 0;
        recipe->wg_total_alkalinity = 0;
        recipe->wg_ph = 7;
        recipe->wb_calcium = 0;
        recipe->wb_sulfate = 0;
        recipe->wb_chloride = 0;
        recipe->wb_sodium = 0;
        recipe->wb_magnesium = 0;
        recipe->wb_total_alkalinity = 0;
        recipe->wb_ph = 7;
	recipe->wa_acid_name = 0;
	recipe->wa_acid_perc = 80;
	recipe->wa_base_name = 0;

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

    ui->lockedEdit->setChecked(recipe->locked);
    ui->st_nameEdit->setText(recipe->st_name);
    ui->st_groupEdit->setText(recipe->st_letter);
    ui->st_guideEdit->setText(recipe->st_guide);
    ui->st_catEdit->setText(recipe->st_category);
    ui->st_catnrEdit->setText(QString("%1").arg(recipe->st_category_number));
    ui->st_typeEdit->setText(s_types[recipe->st_type]);

    ui->nameEdit->setText(recipe->name);
    ui->notesEdit->setPlainText(recipe->notes);
    ui->typeEdit->setCurrentIndex(recipe->type);
    ui->batch_sizeEdit->setValue(recipe->batch_size);
    ui->boil_sizeEdit->setValue(recipe->boil_size);
    ui->boil_timeEdit->setValue(recipe->boil_time);
    ui->efficiencyEdit->setValue(recipe->efficiency);

    ui->est_ogEdit->setValue(recipe->est_og);
    ui->est_og2Edit->setValue(recipe->est_og);
    ui->est_og3Edit->setValue(recipe->est_og);
    ui->est_ogShow->setRange(recipe->st_og_min, recipe->st_og_max);
    ui->est_ogShow->setPrecision(3);
    ui->est_ogShow->setMarkerTextIsValue(true);
    ui->est_ogShow->setValue(recipe->est_og);

    ui->est_fgEdit->setValue(recipe->est_fg);
    ui->est_fg3Edit->setValue(recipe->est_fg);
    ui->est_fgShow->setRange(recipe->st_fg_min, recipe->st_fg_max);
    ui->est_fgShow->setPrecision(3);
    ui->est_fgShow->setMarkerTextIsValue(true);
    ui->est_fgShow->setValue(recipe->est_fg);

    ui->est_abvEdit->setValue(recipe->est_abv);
    ui->est_abv2Edit->setValue(recipe->est_abv);
    ui->est_abvShow->setRange(recipe->st_abv_min, recipe->st_abv_max);
    ui->est_abvShow->setPrecision(1);
    ui->est_abvShow->setMarkerTextIsValue(true);
    ui->est_abvShow->setValue(recipe->est_abv);

    ui->est_colorEdit->setValue(recipe->est_color);
    ui->est_colorEdit->setStyleSheet(Utils::ebc_to_style(recipe->est_color));
    ui->est_color2Edit->setValue(recipe->est_color);
    ui->est_color2Edit->setStyleSheet(Utils::ebc_to_style(recipe->est_color));
    ui->est_colorShow->setPrecision(0);
    ui->est_colorShow->setMarkerTextIsValue(true);
    ui->est_colorShow->setRange(recipe->st_color_min, recipe->st_color_max);
    ui->est_colorShow->setValue(recipe->est_color);

    ui->color_methodEdit->setCurrentIndex(recipe->color_method);

    ui->est_ibuEdit->setValue(recipe->est_ibu);
    ui->est_ibu2Edit->setValue(recipe->est_ibu);
    ui->est_ibuShow->setPrecision(0);
    ui->est_ibuShow->setMarkerTextIsValue(true);
    ui->est_ibuShow->setRange(recipe->st_ibu_min, recipe->st_ibu_max);
    ui->est_ibuShow->setValue(recipe->est_ibu);

    ui->ibu_methodEdit->setCurrentIndex(recipe->ibu_method);

    ui->est_carbEdit->setValue(recipe->est_carb);
    ui->est_carbShow->setPrecision(1);
    ui->est_carbShow->setMarkerTextIsValue(true);
    ui->est_carbShow->setRange(recipe->st_carb_min, recipe->st_carb_max);
    ui->est_carbShow->setValue(recipe->est_carb);

    // 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"
    ui->fermentablesTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
    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);

    // 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";
}


bool EditRecipe::ferment_sort_test(const Fermentables &D1, const Fermentables &D2)
{
    return (D1.f_added <= D2.f_added) && (D1.f_amount >= D2.f_amount) && (D1.f_color < D2.f_color);
}


void EditRecipe::refreshFermentables()
{
    QString w;
    QWidget* pWidget;
    QHBoxLayout* pLayout;
    QTableWidgetItem *item;
    QLabel *label;

    qDebug() << "refreshFermentables" << recipe->fermentables.size();
    std::sort(recipe->fermentables.begin(), recipe->fermentables.end(), ferment_sort_test);

    /*
     * 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("Supplier"), tr("Fermentable"), tr("EBC"), tr("Type"), tr("Graintype"), tr("When"), tr("Yield"),
		    	      tr("Amount"), tr("Procent"), tr("100%"), tr("Delete"), tr("Edit") });
    ui->fermentablesTable->setColumnCount(12);
    ui->fermentablesTable->setColumnWidth(0, 150);     /* Supplier	*/
    ui->fermentablesTable->setColumnWidth(1, 225);     /* Fermentable	*/
    ui->fermentablesTable->setColumnWidth(2,  50);     /* Color		*/
    ui->fermentablesTable->setColumnWidth(3,  75);     /* Type		*/
    ui->fermentablesTable->setColumnWidth(4,  75);     /* Graintype	*/
    ui->fermentablesTable->setColumnWidth(5,  75);     /* Added		*/
    ui->fermentablesTable->setColumnWidth(6,  60);     /* Yield		*/
    ui->fermentablesTable->setColumnWidth(7,  90);     /* Amount	*/
    ui->fermentablesTable->setColumnWidth(8,  60);     /* Procent	*/
    ui->fermentablesTable->setColumnWidth(9,  50);     /* 100%		*/
    ui->fermentablesTable->setColumnWidth(10, 80);     /* Delete	*/
    ui->fermentablesTable->setColumnWidth(11, 80);     /* Edit		*/
    ui->fermentablesTable->setHorizontalHeaderLabels(labels);
    ui->fermentablesTable->verticalHeader()->hide();
    ui->fermentablesTable->setRowCount(recipe->fermentables.size());

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

	ui->fermentablesTable->setItem(i, 0, new QTableWidgetItem(recipe->fermentables.at(i).f_supplier));
	ui->fermentablesTable->setItem(i, 1, new QTableWidgetItem(recipe->fermentables.at(i).f_name));

        w = QString("%1").arg(recipe->fermentables.at(i).f_color, 1, 'f', 0, '0');
        item = new QTableWidgetItem(w);
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        ui->fermentablesTable->setItem(i, 2, item);

	item = new QTableWidgetItem(f_types[recipe->fermentables.at(i).f_type]);
	item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
	ui->fermentablesTable->setItem(i, 3, item);

        item = new QTableWidgetItem(f_graintypes[recipe->fermentables.at(i).f_graintype]);
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        ui->fermentablesTable->setItem(i, 4, item);

        item = new QTableWidgetItem(f_added[recipe->fermentables.at(i).f_added]);
        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
        ui->fermentablesTable->setItem(i, 5, item);

        item = new QTableWidgetItem(QString("%1%").arg(recipe->fermentables.at(i).f_yield, 2, 'f', 1, '0'));
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        ui->fermentablesTable->setItem(i, 6, item);

        item = new QTableWidgetItem(QString("%1 Kg").arg(recipe->fermentables.at(i).f_amount, 4, 'f', 3, '0'));
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        ui->fermentablesTable->setItem(i, 7, item);

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

	if (recipe->fermentables.at(i).f_adjust_to_total_100) {
		pWidget = new QWidget();
            	label = new QLabel;
            	label->setPixmap(QPixmap(":icons/silk/tick.png"));
            	pLayout = new QHBoxLayout(pWidget);
            	pLayout->addWidget(label);
            	pLayout->setAlignment(Qt::AlignCenter);
            	pLayout->setContentsMargins(0, 0, 0, 0);
            	pWidget->setLayout(pLayout);
            	ui->fermentablesTable->setCellWidget(i, 9, pWidget);
	} else {
		ui->fermentablesTable->removeCellWidget(i, 9);
	}

	/* 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(on_deleteFermentRow_clicked()));
        pLayout = new QHBoxLayout(pWidget);
        pLayout->addWidget(btn_dele);
        pLayout->setContentsMargins(5, 0, 5, 0);
        pWidget->setLayout(pLayout);
        ui->fermentablesTable->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(on_editFermentRow_clicked()));
	pLayout = new QHBoxLayout(pWidget);
        pLayout->addWidget(btn_edit);
	pLayout->setContentsMargins(5, 0, 5, 0);
        pWidget->setLayout(pLayout);
        ui->fermentablesTable->setCellWidget(i, 11, 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;
    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 (recipe->mashs.size() > 0) {
	for (i = 0; i < recipe->mashs.size(); i++) {
	    if (recipe->mashs.at(i).step_type == 0)		// Infusion
		mashinfuse += recipe->mashs.at(i).step_infuse_amount;
	    if (recipe->mashs.at(i).step_temp < 75) {		// Ignore mashout
		mashtime += recipe->mashs.at(i).step_time;
		mashtemp += recipe->mashs.at(i).step_time * recipe->mashs.at(i).step_temp;
	    }
	}
	mashtemp = mashtemp / mashtime;
	mvol = mashinfuse;
	qDebug() << "  mash time" << mashtime << "temp" << mashtemp << "infuse" << mashinfuse;
    } else {
	qDebug() << "  no mash schedule";
    }

    if (recipe->fermentables.size() < 1) {
	qDebug() << "  no fermentables, return.";
	return;
    }
    qDebug() << "  adjust to 100" << recipe->fermentables_use100;

    for (i = 0; i < recipe->fermentables.size(); i++) {
	if (recipe->fermentables.at(i).f_type == 1 && recipe->fermentables.at(i).f_added < 4)		// Sugars
	    psugar += recipe->fermentables.at(i).f_percentage;
	if (recipe->fermentables.at(i).f_graintype == 2 && recipe->fermentables.at(i).f_added < 4)	// Crystal/Cara
	    pcara += recipe->fermentables.at(i).f_percentage;
	d = recipe->fermentables.at(i).f_amount * (recipe->fermentables.at(i).f_yield / 100) * (1 - recipe->fermentables.at(i).f_moisture / 100);
	if (recipe->fermentables.at(i).f_added == 0) {					// Mash
	    if (mvol > 0) {							// If mash volume is known
		mvol += recipe->fermentables.at(i).f_amount * recipe->fermentables.at(i).f_moisture / 100;
		s += d;
	    }
	    d = ui->efficiencyEdit->value() / 100 * d;
	    sugarsm += d;
	    mashkg += recipe->fermentables.at(i).f_amount;
	}
	if (recipe->fermentables.at(i).f_added == 0 || recipe->fermentables.at(i).f_added == 1)		// Mash or boil
	    sugarsf += d;
	if (recipe->fermentables.at(i).f_added == 2 || recipe->fermentables.at(i).f_added == 3) {		// Fermentation or lagering
	    x = (recipe->fermentables.at(i).f_yield / 100) * (1 - recipe->fermentables.at(i).f_moisture / 100);
	    addedS += recipe->fermentables.at(i).f_amount * x;
	    addedmass += recipe->fermentables.at(i).f_amount;
	    vol += (x * sugardensity + (1 - x) * 1) * recipe->fermentables.at(i).f_amount;
	}
	if (recipe->fermentables.at(i).f_added == 0 && (recipe->fermentables.at(i).f_type == 0 || recipe->fermentables.at(i).f_type == 4) && recipe->fermentables.at(i).f_color < 50) {
	    lintner += recipe->fermentables.at(i).f_diastatic_power * recipe->fermentables.at(i).f_amount;
	}
	if (recipe->fermentables.at(i).f_added < 4) {
	    colort += recipe->fermentables.at(i).f_amount * Utils::ebc_to_srm(recipe->fermentables.at(i).f_color);
	    colorh += recipe->fermentables.at(i).f_amount * recipe->fermentables.at(i).f_color * Utils::get_kt(recipe->fermentables.at(i).f_color);
	    colorn += (recipe->fermentables.at(i).f_percentage / 100) * recipe->fermentables.at(i).f_color;	// 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);
    ui->calEdit->setValue(round((alc + sug) / (12 * 0.0295735296)));
}


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 locked=:locked, st_name=:st_name, st_letter=:st_letter, "
		"st_guide=:st_guide, st_category=:st_category, st_category_number=:st_catnr, st_type=:st_type, "
		"st_og_min=:st_og_min, st_og_max=:st_og_max, st_fg_min=:st_fg_min, st_fg_max=:st_fg_max, "
		"st_ibu_min=:st_ibu_min, st_ibu_max=:st_ibu_max, st_color_min=:st_color_min, st_color_max=:st_color_max, "
		"st_carb_min=:st_carb_min, st_carb_max=:st_carb_max, st_abv_min=:st_abv_min, st_abv_max=:st_abv_max, "
		"name=:name, notes:=notes, type=:type, batch_size=:batch_size, boil_size=:boil_size, "
		"boil_time=:boil_time, efficiency=:efficiency, "
		"uuid = :uuid");
    	} else {
	    query.prepare("UPDATE recipes SET locked=:locked, st_name=:st_name, st_letter=:st_letter, "
		"st_guide=:st_guide, st_category=:st_category, st_category_number=:st_catnr, st_type=:st_type, "
                "st_og_min=:st_og_min, st_og_max=:st_og_max, st_fg_min=:st_fg_min, st_fg_max=:st_fg_max, "
                "st_ibu_min=:st_ibu_min, st_ibu_max=:st_ibu_max, st_color_min=:st_color_min, st_color_max=:st_color_max, "
                "st_carb_min=:st_carb_min, st_carb_max=:st_carb_max, st_abv_min=:st_abv_min, st_abv_max=:st_abv_max, "
                "name=:name, notes:=notes, type=:type, batch_size=:batch_size, boil_size=:boil_size, "
		"boil_time=:boil_time, efficiency=:efficiency "
                " WHERE record = :recno");
    	}
	query.bindValue(":locked", ui->lockedEdit->isChecked() ? 1:0);
	query.bindValue(":st_name", ui->st_nameEdit->text());
	query.bindValue(":st_letter", ui->st_groupEdit->text());
	query.bindValue(":st_guide", ui->st_guideEdit->text());
	query.bindValue(":st_category", ui->st_catEdit->text());
	query.bindValue(":st_catnr", recipe->st_type);
	query.bindValue(":st_og_min", QString("%1").arg(ui->est_ogShow->minval(), 4, 'f', 3, '0'));
	query.bindValue(":st_og_max", QString("%1").arg(ui->est_ogShow->maxval(), 4, 'f', 3, '0'));
	query.bindValue(":st_fg_min", QString("%1").arg(ui->est_fgShow->minval(), 4, 'f', 3, '0'));
        query.bindValue(":st_fg_max", QString("%1").arg(ui->est_fgShow->maxval(), 4, 'f', 3, '0'));
	query.bindValue(":st_ibu_min", QString("%1").arg(ui->est_ibuShow->minval(), 4, 'f', 3, '0'));
        query.bindValue(":st_ibu_max", QString("%1").arg(ui->est_ibuShow->maxval(), 4, 'f', 3, '0'));
	query.bindValue(":st_color_min", QString("%1").arg(ui->est_colorShow->minval(), 4, 'f', 3, '0'));
        query.bindValue(":st_color_max", QString("%1").arg(ui->est_colorShow->maxval(), 4, 'f', 3, '0'));
	query.bindValue(":st_carb_min", QString("%1").arg(ui->est_carbShow->minval(), 4, 'f', 3, '0'));
        query.bindValue(":st_carb_max", QString("%1").arg(ui->est_carbShow->maxval(), 4, 'f', 3, '0'));
	query.bindValue(":st_abv_min", QString("%1").arg(ui->est_abvShow->minval(), 4, 'f', 3, '0'));
        query.bindValue(":st_abv_max", QString("%1").arg(ui->est_abvShow->maxval(), 4, 'f', 3, '0'));
	query.bindValue(":name", ui->nameEdit->text());
	query.bindValue(":notes", ui->notesEdit->toPlainText());
	query.bindValue(":type", ui->typeEdit->currentIndex());
	query.bindValue(":batch_size", QString("%1").arg(ui->batch_sizeEdit->value(), 2, 'f', 1, '0'));
	query.bindValue(":boil_size", QString("%1").arg(ui->boil_sizeEdit->value(), 2, 'f', 1, '0'));
	query.bindValue(":boil_time", QString("%1").arg(ui->boil_timeEdit->value(), 2, 'f', 1, '0'));
	query.bindValue(":efficiency", QString("%1").arg(ui->efficiencyEdit->value(), 2, 'f', 1, '0'));
	query.bindValue(":est_og", QString("%1").arg(ui->est_ogEdit->value(), 4, 'f', 3, '0'));
	query.bindValue(":est_fg", QString("%1").arg(ui->est_fgEdit->value(), 4, 'f', 3, '0'));
	query.bindValue(":est_abv", QString("%1").arg(ui->est_abvEdit->value(), 2, 'f', 1, '0'));
	query.bindValue(":est_color", QString("%1").arg(ui->est_colorEdit->value(), 1, 'f', 0, '0'));
	query.bindValue(":color_method", ui->color_methodEdit->currentIndex());
	query.bindValue(":est_ibu", QString("%1").arg(ui->est_ibuEdit->value(), 1, 'f', 0, '0'));
	query.bindValue(":ibu_method", ui->ibu_methodEdit->currentIndex());
	query.bindValue(":est_carb", QString("%1").arg(ui->est_carbEdit->value(), 2, 'f', 1, '0'));

	if (this->recno == -1) {
	    query.bindValue(":uuid", QUuid::createUuid().toString().mid(1, 36));
	} else {
	    query.bindValue(":recno", this->recno);
	}
	qDebug() << query.lastQuery();
//	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.
    recipe->st_name = query.value(1).toString();
    recipe->st_category = query.value(2).toString();
    recipe->st_category_number = query.value(3).toInt();
    recipe->st_letter = query.value(4).toString();
    recipe->st_guide = query.value(5).toString();
    recipe->st_type = query.value(6).toInt();
    recipe->st_og_min = query.value(7).toDouble();
    recipe->st_og_max = query.value(8).toDouble();
    recipe->st_fg_min = query.value(9).toDouble();
    recipe->st_fg_max = query.value(10).toDouble();
    recipe->st_ibu_min = query.value(11).toDouble();
    recipe->st_ibu_max = query.value(12).toDouble();
    recipe->st_color_min = query.value(13).toDouble();
    recipe->st_color_max = query.value(14).toDouble();
    recipe->st_carb_min = query.value(15).toDouble();
    recipe->st_carb_max = query.value(16).toDouble();
    recipe->st_abv_min = query.value(17).toDouble();
    recipe->st_abv_max = query.value(18).toDouble();

    ui->st_nameEdit->setText(recipe->st_name);
    ui->st_groupEdit->setText(recipe->st_letter);
    ui->st_guideEdit->setText(recipe->st_guide);
    ui->st_catEdit->setText(recipe->st_category);
    ui->st_catnrEdit->setText(QString("%1").arg(recipe->st_category_number));
    ui->st_typeEdit->setText(s_types[recipe->st_type]);

    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();
}


/* TODO: move this code to MySQL save */
void EditRecipe::fermentable_Json()
{
    QTableWidgetItem *item;
    QJsonArray array;

    qDebug() << "fermentable_Json()";

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

        QJsonObject obj;
	obj.insert("f_acid_to_ph_57", recipe->fermentables.at(i).f_acid_to_ph_57);
        obj.insert("f_add_after_boil", recipe->fermentables.at(i).f_add_after_boil);
        obj.insert("f_added", recipe->fermentables.at(i).f_added);
        obj.insert("f_adjust_to_total_100", recipe->fermentables.at(i).f_adjust_to_total_100);
        obj.insert("f_amount", recipe->fermentables.at(i).f_added);
        obj.insert("f_coarse_fine_diff", recipe->fermentables.at(i).f_coarse_fine_diff);
        obj.insert("f_color", recipe->fermentables.at(i).f_color);
        obj.insert("f_cost", recipe->fermentables.at(i).f_cost);
        obj.insert("f_di_ph", recipe->fermentables.at(i).f_di_ph);
        obj.insert("f_diastatic_power", recipe->fermentables.at(i).f_diastatic_power);
        obj.insert("f_dissolved_protein", recipe->fermentables.at(i).f_dissolved_protein);
        obj.insert("f_graintype", recipe->fermentables.at(i).f_graintype);
        obj.insert("f_max_in_batch", recipe->fermentables.at(i).f_max_in_batch);
        obj.insert("f_moisture", recipe->fermentables.at(i).f_moisture);
        obj.insert("f_name", recipe->fermentables.at(i).f_name);
        obj.insert("f_origin", recipe->fermentables.at(i).f_origin);
        obj.insert("f_percentage", recipe->fermentables.at(i).f_percentage);
        obj.insert("f_protein", recipe->fermentables.at(i).f_protein);
        obj.insert("f_recommend_mash", recipe->fermentables.at(i).f_recommend_mash);
        obj.insert("f_supplier", recipe->fermentables.at(i).f_supplier);
        obj.insert("f_type", recipe->fermentables.at(i).f_type);
        obj.insert("f_yield", recipe->fermentables.at(i).f_yield);
	qDebug() << "fermentable_Json" << i << obj;
	array.append(obj);      /* Append this object */
    }

    qDebug() << array;
    /* Copy to the global array and refresh */
//    this->fermentables.setArray(array);
}


void EditRecipe::on_deleteFermentRow_clicked()
{
    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
    int row = pb->objectName().toInt();
    qDebug() << "Delete fermentable row" << row;

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

    ui->fermentablesTable->removeRow(row);
//    recalculate percentages
//    fermentable_Json();
}


void EditRecipe::ferment_amount_changed(double val)
{
    QTableWidgetItem *item;
    double	total = 0, perc;

    if (recipe->fermentables_use100)
	return;

    qDebug() << "ferment_amount_changed()" << editrow << val;
    this->ignoreChanges = true;

    recipe->fermentables[editrow].f_amount = val;
    item = new QTableWidgetItem(QString("%1 Kg").arg(val, 4, 'f', 3, '0'));
    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
    ui->fermentablesTable->setItem(editrow, 7, item);

    for (int i = 0; i < recipe->fermentables.size(); i++)
	total += recipe->fermentables.at(i).f_amount;

    /*
     * Recalculate the percentages
     */
    for (int i = 0; i < recipe->fermentables.size(); i++) {
	perc = recipe->fermentables.at(i).f_amount / total * 100;
	recipe->fermentables[i].f_percentage = perc;
	item = new QTableWidgetItem(QString("%1%").arg(perc, 2, 'f', 1, '0'));
        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
        ui->fermentablesTable->setItem(i, 8, item);
	if (i == editrow)
	    this->pctEdit->setValue(perc);
    }
    this->ignoreChanges = false;
}

void EditRecipe::ferment_pct_changed(double val)
{
    if (! recipe->fermentables_use100)
        return;

    qDebug() << "ferment_pct_changed()" << val;
}


void EditRecipe::on_editFermentRow_clicked()
{
    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
    editrow = pb->objectName().toInt();
    qDebug() << "Edit fermentable row" << editrow;
    Fermentables backup = recipe->fermentables.at(editrow);

    QDialog* dialog = new QDialog(this);
    dialog->resize(738, 287);
    QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
    buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
    buttonBox->setGeometry(QRect(30, 240, 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 ingredient:"));
    nameLabel->setGeometry(QRect(10, 10, 141, 20));
    nameLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *supplierLabel = new QLabel(dialog);
    supplierLabel->setObjectName(QString::fromUtf8("supplierLabel"));
    supplierLabel->setText(tr("Supplier:"));
    supplierLabel->setGeometry(QRect(10, 40, 141, 20));
    supplierLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *amountLabel = new QLabel(dialog);
    amountLabel->setObjectName(QString::fromUtf8("amountLabel"));
    amountLabel->setText(tr("Amount in kg:"));
    amountLabel->setGeometry(QRect(10, 100, 141, 20));
    amountLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *pctLabel = new QLabel(dialog);
    pctLabel->setObjectName(QString::fromUtf8("pctLabel"));
    pctLabel->setText(tr("Percentage in batch:"));
    pctLabel->setGeometry(QRect(10, 130, 141, 20));
    pctLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *to100Label = new QLabel(dialog);
    to100Label->setObjectName(QString::fromUtf8("to100Label"));
    to100Label->setText(tr("Auto fill to 100%:"));
    to100Label->setGeometry(QRect(10, 160, 141, 20));
    to100Label->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *addedLabel = new QLabel(dialog);
    addedLabel->setObjectName(QString::fromUtf8("addedLabel"));
    addedLabel->setText(tr("Use at:"));
    addedLabel->setGeometry(QRect(10, 190, 141, 20));
    addedLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *selectLabel = new QLabel(dialog);
    selectLabel->setObjectName(QString::fromUtf8("selectLabel"));
    selectLabel->setText(tr("Select ingredient:"));
    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(420, 70, 121, 20));
    instockLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    QLabel *maxLabel = new QLabel(dialog);
    maxLabel->setObjectName(QString::fromUtf8("maxLabel"));
    maxLabel->setText(tr("Max in batch:"));
    maxLabel->setGeometry(QRect(420, 130, 121, 20));
    maxLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);

    selectEdit = new QComboBox(dialog);
    selectEdit->setObjectName(QString::fromUtf8("selectEdit"));
    selectEdit->setGeometry(QRect(160, 70, 251, 23));
    nameEdit = new QLineEdit(dialog);
    nameEdit->setObjectName(QString::fromUtf8("nameEdit"));
    nameEdit->setText(recipe->fermentables.at(editrow).f_name);
    nameEdit->setGeometry(QRect(160, 10, 511, 23));
    nameEdit->setReadOnly(true);
    supplierEdit = new QLineEdit(dialog);
    supplierEdit->setObjectName(QString::fromUtf8("supplierEdit"));
    supplierEdit->setText(recipe->fermentables.at(editrow).f_supplier);
    supplierEdit->setGeometry(QRect(160, 40, 511, 23));
    supplierEdit->setReadOnly(true);
    amountEdit = new QDoubleSpinBox(dialog);
    amountEdit->setObjectName(QString::fromUtf8("amountEdit"));
    amountEdit->setGeometry(QRect(160, 100, 121, 24));
    amountEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    amountEdit->setAccelerated(true);
    amountEdit->setDecimals(3);
    amountEdit->setReadOnly(recipe->fermentables_use100);
    amountEdit->setMaximum(100000.0);
    amountEdit->setSingleStep(0.0010);
    amountEdit->setValue(recipe->fermentables.at(editrow).f_amount);
    connect(amountEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditRecipe::ferment_amount_changed);

    pctEdit = new QDoubleSpinBox(dialog);
    pctEdit->setObjectName(QString::fromUtf8("pctEdit"));
    pctEdit->setGeometry(QRect(160, 130, 121, 24));
    pctEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    pctEdit->setAccelerated(true);
    pctEdit->setDecimals(1);
    if (recipe->fermentables_use100) {
    	if (recipe->fermentables.at(editrow).f_adjust_to_total_100)
	    pctEdit->setReadOnly(true);
    	else
    	    pctEdit->setReadOnly(false);
    } else {
	pctEdit->setReadOnly(true);
    }
    pctEdit->setMaximum(100.0);
    pctEdit->setSingleStep(0.1);
    pctEdit->setValue(recipe->fermentables.at(editrow).f_percentage);
    connect(pctEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditRecipe::ferment_pct_changed);

    addedEdit = new QComboBox(dialog);
    addedEdit->setObjectName(QString::fromUtf8("addedEdit"));
    addedEdit->setGeometry(QRect(160, 190, 161, 23));
    addedEdit->addItem(tr("Mash"));
    addedEdit->addItem(tr("Boil"));
    addedEdit->addItem(tr("Fermentation"));
    addedEdit->addItem(tr("Lagering"));
    addedEdit->addItem(tr("Bottle"));
    addedEdit->addItem(tr("Kegs"));
    addedEdit->setCurrentIndex(recipe->fermentables.at(editrow).f_added);

    to100Edit = new QCheckBox(dialog);
    to100Edit->setObjectName(QString::fromUtf8("to100Edit"));
    to100Edit->setGeometry(QRect(160, 160, 85, 21));
    to100Edit->setChecked(recipe->fermentables.at(editrow).f_adjust_to_total_100);

    instockEdit = new QCheckBox(dialog);
    instockEdit->setObjectName(QString::fromUtf8("instockEdit"));
    instockEdit->setGeometry(QRect(550, 70, 85, 21));

    maxEdit = new QDoubleSpinBox(dialog);
    maxEdit->setObjectName(QString::fromUtf8("maxEdit"));
    maxEdit->setGeometry(QRect(550, 130, 121, 24));
    maxEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
    maxEdit->setReadOnly(true);
    maxEdit->setButtonSymbols(QAbstractSpinBox::NoButtons);
    maxEdit->setDecimals(1);
    maxEdit->setValue(recipe->fermentables.at(editrow).f_max_in_batch);

    connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));
    connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));
    dialog->exec();
    if (dialog->result() == QDialog::Rejected) {
	qDebug() << "rejected";
	// restore fermentbackup
	// recalculate percentages
    } else {
	qDebug() << "accepted";
	// fermentrow to final

	//fermentable_Json(); segfault !
    }

    // disconnect
    // return
    emit refreshAll();
}


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