Initial import of EditProduct, the part from EditRecipe is ported.

Thu, 28 Apr 2022 22:49:13 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Thu, 28 Apr 2022 22:49:13 +0200
changeset 175
f1ed3a2a94e9
parent 174
ceb8aa4ebd25
child 176
69a033e099a2

Initial import of EditProduct, the part from EditRecipe is ported.

CMakeLists.txt file | annotate | diff | comparison | revisions
src/EditProduct.cpp file | annotate | diff | comparison | revisions
src/EditProduct.h file | annotate | diff | comparison | revisions
src/EditProductExport.cpp file | annotate | diff | comparison | revisions
src/EditProductTab1.cpp file | annotate | diff | comparison | revisions
src/EditProductTab10.cpp file | annotate | diff | comparison | revisions
src/EditProductTab11.cpp file | annotate | diff | comparison | revisions
src/EditProductTab12.cpp file | annotate | diff | comparison | revisions
src/EditProductTab2.cpp file | annotate | diff | comparison | revisions
src/EditProductTab3.cpp file | annotate | diff | comparison | revisions
src/EditProductTab4.cpp file | annotate | diff | comparison | revisions
src/EditProductTab5.cpp file | annotate | diff | comparison | revisions
src/EditProductTab6.cpp file | annotate | diff | comparison | revisions
src/EditProductTab7.cpp file | annotate | diff | comparison | revisions
src/EditProductTab8.cpp file | annotate | diff | comparison | revisions
src/EditProductTab9.cpp file | annotate | diff | comparison | revisions
src/PrinterDialog.cpp file | annotate | diff | comparison | revisions
src/ProdInprod.cpp file | annotate | diff | comparison | revisions
src/global.cpp file | annotate | diff | comparison | revisions
src/global.h file | annotate | diff | comparison | revisions
ui/EditProduct.ui file | annotate | diff | comparison | revisions
--- a/CMakeLists.txt	Thu Apr 28 16:59:58 2022 +0200
+++ b/CMakeLists.txt	Thu Apr 28 22:49:13 2022 +0200
@@ -8,8 +8,8 @@
 # ===== Set application version =====
 
 SET( bmsapp_VERSION_MAJOR 0 )
-SET( bmsapp_VERSION_MINOR 1 )
-SET( bmsapp_VERSION_PATCH 6 )
+SET( bmsapp_VERSION_MINOR 2 )
+SET( bmsapp_VERSION_PATCH 0 )
 
 # Compile flags
 
@@ -180,6 +180,7 @@
     ${SRCDIR}/EditProfileFerment.cpp
     ${SRCDIR}/EditRecipe.cpp
     ${SRCDIR}/ProdInprod.cpp
+    ${SRCDIR}/EditProduct.cpp
     ${SRCDIR}/Setup.cpp
     ${SRCDIR}/Utils.cpp
     ${SRCDIR}/PrinterDialog.cpp
@@ -217,6 +218,7 @@
     ${SRCDIR}/EditProfileFerment.h
     ${SRCDIR}/EditRecipe.h
     ${SRCDIR}/ProdInprod.h
+    ${SRCDIR}/EditProduct.h
     ${SRCDIR}/Setup.h
     ${SRCDIR}/Utils.h
     ${SRCDIR}/PrinterDialog.h
@@ -241,6 +243,7 @@
     ${UIDIR}/EditProfileStyle.ui
     ${UIDIR}/EditProfileFerment.ui
     ${UIDIR}/EditRecipe.ui
+    ${UIDIR}/EditProduct.ui
     ${UIDIR}/MainWindow.ui
   )
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EditProduct.cpp	Thu Apr 28 22:49:13 2022 +0200
@@ -0,0 +1,1342 @@
+/**
+ * EditProduct.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 "EditProduct.h"
+#include "PrinterDialog.h"
+#include "../ui/ui_EditProduct.h"
+#include "Utils.h"
+#include "global.h"
+
+
+
+EditProduct::EditProduct(int id, QWidget *parent) : QDialog(parent), ui(new Ui::EditProduct)
+{
+    QSqlQuery query, wquery, yquery;
+
+    qDebug() << "EditProduct record:" << id;
+    product = new Product;
+    ui->setupUi(this);
+    product->fermentables_row = product->hops_row = product->miscs_row = product->yeasts_row = product->mashs_row = -1;
+    product->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");
+
+    for (int i = 0; i < my_acids.size(); i++) {
+	ui->mw_acidPick->addItem(my_acids.at(i).name_en);
+	ui->sp_acidtypeEdit->addItem(my_acids.at(i).name_en);
+    }
+
+    ui->sp_sourceEdit->addItem(tr("Source 1"));
+    ui->sp_sourceEdit->addItem(tr("Source 2"));
+    ui->sp_sourceEdit->addItem(tr("Mixed"));
+
+    query.prepare("SELECT name FROM inventory_waters ORDER BY record");
+    query.exec();
+    query.first();
+    ui->w1_nameEdit->addItem("");
+    ui->w2_nameEdit->addItem("");
+    for (int i = 0; i < query.size(); i++) {
+	ui->w1_nameEdit->addItem(query.value(0).toString());
+	ui->w2_nameEdit->addItem(query.value(0).toString());
+	query.next();
+    }
+
+    query.prepare("SELECT name FROM profile_water ORDER BY name");
+    query.exec();
+    ui->wt_sourceEdit->addItem("");
+    while (query.next()) {
+        ui->wt_sourceEdit->addItem(query.value(0).toString());
+    }
+
+    query.prepare("SELECT name FROM profile_mash ORDER BY name");
+    query.exec();
+    ui->mash_pickEdit->addItem("");
+    while (query.next()) {
+	ui->mash_pickEdit->addItem(query.value(0).toString());
+    }
+
+    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 products WHERE record = :recno");
+	query.bindValue(":recno", id);
+	query.exec();
+	if (! query.first()) {
+	    qDebug() << "EditProduct seek error record" << id;
+	    QMessageBox::warning(this, tr("Database error"), tr("MySQL error: record %1 not found").arg(id));
+	    this->done(QDialog::Rejected);	// At this stage, this doesn't work because the dialog is not yet visible.
+	    return;
+	}
+
+	QSqlRecord rec = query.record();
+	for (int i = 0; i < rec.count(); i++)
+            qDebug() << i << rec.fieldName(i) << query.value(i);
+
+	product->record = query.value("record").toInt();
+	product->uuid = query.value("uuid").toString();
+	product->name = query.value("name").toString();
+	product->code = query.value("code").toString();
+	product->birth = query.value("birth").toDate();
+	product->stage = query.value("stage").toInt();
+        product->notes = query.value("notes").toString();
+	product->log_brew = query.value("log_brew").toInt() ? true:false;
+	product->log_fermentation = query.value("log_fermentation").toInt() ? true:false;
+	product->log_ispindel = query.value("log_ispindel").toInt() ? true:false;
+	product->log_co2pressure = query.value("log_co2pressure").toInt() ? true:false;
+	product->inventory_reduced = query.value("inventory_reduced").toInt();
+	product->locked = query.value("locked").toInt() ? true:false;
+
+	product->eq_name = query.value("eq_name").toString();
+	product->eq_notes = query.value("eq_notes").toString();
+	product->eq_boil_size = query.value("eq_boil_size").toDouble();
+	product->eq_batch_size = query.value("eq_batch_size").toDouble();
+	product->eq_tun_volume = query.value("eq_tun_volume").toDouble();
+	product->eq_tun_weight = query.value("eq_tun_weight").toDouble();
+	product->eq_tun_specific_heat = query.value("eq_tun_specific_heat").toDouble();
+	product->eq_tun_material = query.value("eq_tun_material").toInt();
+	product->eq_tun_height = query.value("eq_tun_height").toDouble();
+	product->eq_top_up_water = query.value("eq_top_up_water").toDouble();
+	product->eq_trub_chiller_loss = query.value("eq_trub_chiller_loss").toDouble();
+	product->eq_evap_rate = query.value("eq_evap_rate").toDouble();
+	product->eq_boil_time = query.value("eq_boil_time").toDouble();
+	product->eq_calc_boil_volume = query.value("eq_calc_boil_volume").toInt() ? true:false;
+	product->eq_top_up_kettle = query.value("eq_top_up_kettle").toDouble();
+	product->eq_hop_utilization = query.value("eq_hop_utilization").toDouble();
+	product->eq_lauter_volume = query.value("eq_lauter_volume").toDouble();
+	product->eq_lauter_height = query.value("eq_lauter_height").toDouble();
+	product->eq_lauter_deadspace = query.value("eq_lauter_deadspace").toDouble();
+	product->eq_kettle_volume = query.value("eq_kettle_volume").toDouble();
+	product->eq_kettle_height = query.value("eq_kettle_height").toDouble();
+	product->eq_mash_volume = query.value("eq_mash_volume").toDouble();
+	product->eq_mash_max = query.value("eq_mash_max").toDouble();
+	product->eq_efficiency = query.value("eq_efficiency").toDouble();
+
+	product->brew_date_start = query.value("brew_date_start").toDateTime();
+	product->brew_mash_ph = query.value("brew_mash_ph").toDouble();
+	product->brew_mash_sg = query.value("brew_mash_sg").toDouble();
+	product->brew_mash_efficiency = query.value("brew_mash_efficiency").toDouble();
+	product->brew_sparge_temperature = query.value("brew_sparge_temperature").toDouble();
+	product->brew_sparge_volume = query.value("brew_sparge_volume").toDouble();
+	product->brew_sparge_est = query.value("brew_sparge_est").toDouble();
+	product->brew_sparge_ph = query.value("brew_sparge_ph").toDouble();
+	product->brew_preboil_volume = query.value("brew_preboil_volume").toDouble();
+	product->brew_preboil_sg = query.value("brew_preboil_sg").toDouble();
+	product->brew_preboil_ph = query.value("brew_preboil_ph").toDouble();
+	product->brew_preboil_efficiency = query.value("brew_preboil_efficiency").toDouble();
+	product->brew_aboil_volume = query.value("brew_aboil_volume").toDouble();
+	product->brew_aboil_sg = query.value("brew_aboil_sg").toDouble();
+	product->brew_aboil_ph = query.value("brew_aboil_ph").toDouble();
+	product->brew_aboil_efficiency = query.value("brew_aboil_efficiency").toDouble();
+	product->brew_cooling_method = query.value("brew_cooling_method").toInt();
+	product->brew_cooling_time = query.value("brew_cooling_time").toDouble();
+	product->brew_cooling_to = query.value("brew_cooling_to").toDouble();
+	product->brew_whirlpool9 = query.value("brew_whirlpool9").toDouble();
+	product->brew_whirlpool7 = query.value("brew_whirlpool7").toDouble();
+	product->brew_whirlpool6 = query.value("brew_whirlpool6").toDouble();
+	product->brew_whirlpool2 = query.value("brew_whirlpool2").toDouble();
+	product->brew_fermenter_volume = query.value("brew_fermenter_volume").toDouble();
+	product->brew_fermenter_extrawater = query.value("brew_fermenter_extrawater").toDouble();
+	product->brew_fermenter_tcloss = query.value("brew_fermenter_tcloss").toDouble();
+	product->brew_aeration_time = query.value("brew_aeration_time").toDouble();
+	product->brew_aeration_speed = query.value("brew_aeration_speed").toDouble();
+	product->brew_aeration_type = query.value("brew_aeration_type").toInt();
+	product->brew_fermenter_sg = query.value("brew_fermenter_sg").toDouble();
+	product->brew_fermenter_ibu = query.value("brew_fermenter_ibu").toDouble();
+	product->brew_fermenter_color = query.value("brew_fermenter_color").toDouble();
+	product->brew_date_end = query.value("brew_date_end").toDateTime();
+
+	product->og = query.value("og").toDouble();
+	product->fg = query.value("fg").toDouble();
+	product->primary_start_temp = query.value("primary_start_temp").toDouble();
+	product->primary_max_temp = query.value("primary_max_temp").toDouble();
+	product->primary_end_temp = query.value("primary_end_temp").toDouble();
+	product->primary_end_sg = query.value("primary_end_sg").toDouble();
+	product->primary_end_date = query.value("primary_end_date").toDate();
+	product->secondary_temp = query.value("secondary_temp").toDouble();
+	product->secondary_end_sg = query.value("secondary_end_sg").toDouble();
+	product->secondary_end_date = query.value("secondary_end_date").toDate();
+	product->tertiary_temp = query.value("tertiary_temp").toDouble();
+
+	product->package_date = query.value("package_date").toDate();
+	product->package_volume = query.value("package_volume").toDouble();
+	product->package_infuse_amount = query.value("package_infuse_amount").toDouble();
+	product->package_infuse_abv = query.value("package_infuse_abv").toDouble();
+	product->package_infuse_notes = query.value("package_infuse_notes").toString();
+	product->package_abv = query.value("package_abv").toDouble();
+	product->package_ph = query.value("package_ph").toDouble();
+
+	product->bottle_amount = query.value("bottle_amount").toDouble();
+	product->bottle_carbonation = query.value("bottle_carbonation").toDouble();
+	product->bottle_priming_sugar = query.value("bottle_priming_sugar").toInt();
+	product->bottle_priming_amount = query.value("bottle_priming_amount").toDouble();
+	product->bottle_priming_water = query.value("bottle_priming_water").toDouble();
+	product->bottle_carbonation_temp = query.value("bottle_carbonation_temp").toDouble();
+
+	product->keg_amount = query.value("keg_amount").toDouble();
+	product->keg_carbonation = query.value("keg_carbonation").toDouble();
+	product->keg_priming_sugar = query.value("keg_priming_sugar").toInt();
+	product->keg_priming_amount = query.value("keg_priming_amount").toDouble();
+	product->keg_priming_water = query.value("keg_priming_water").toDouble();
+	product->keg_carbonation_temp = query.value("keg_carbonation_temp").toDouble();
+	product->keg_forced_carb = query.value("keg_forced_carb").toInt() ? true:false;
+	product->keg_pressure = query.value("keg_pressure").toDouble();
+
+	product->taste_notes = query.value("taste_notes").toString();
+	product->taste_rate = query.value("taste_rate").toDouble();
+	product->taste_date = query.value("taste_date").toDate();
+	product->taste_color = query.value("taste_color").toString();
+	product->taste_transparency = query.value("taste_transparency").toString();
+	product->taste_head = query.value("taste_head").toString();
+	product->taste_aroma = query.value("taste_aroma").toString();
+	product->taste_taste = query.value("taste_taste").toString();
+	product->taste_mouthfeel = query.value("taste_mouthfeel").toString();
+	product->taste_aftertaste = query.value("taste_aftertaste").toString();
+
+	product->st_name = query.value("st_name").toString();
+	product->st_letter = query.value("st_letter").toString();
+	product->st_guide = query.value("st_guide").toString();
+	product->st_category = query.value("st_category").toString();
+	product->st_category_number = query.value("st_category_number").toInt();
+	product->st_type = query.value("st_type").toInt();
+	product->st_og_min = query.value("st_og_min").toDouble();
+	product->st_og_max = query.value("st_og_max").toDouble();
+	product->st_fg_min = query.value("st_fg_min").toDouble();
+	product->st_fg_max = query.value("st_fg_max").toDouble();
+	product->st_ibu_min = query.value("st_ibu_min").toDouble();
+	product->st_ibu_max = query.value("st_ibu_max").toDouble();
+	product->st_color_min = query.value("st_color_min").toDouble();
+	product->st_color_max = query.value("st_color_max").toDouble();
+	product->st_carb_min = query.value("st_carb_min").toDouble();
+	product->st_carb_max = query.value("st_carb_max").toDouble();
+	product->st_abv_min = query.value("st_abv_min").toDouble();
+	product->st_abv_max = query.value("st_abv_max").toDouble();
+
+	product->type = query.value("type").toInt();
+	product->batch_size = query.value("batch_size").toDouble();
+	product->boil_size = query.value("boil_size").toDouble();
+	product->boil_time = query.value("boil_time").toDouble();
+	product->efficiency = query.value("efficiency").toDouble();
+	product->est_og = query.value("est_og").toDouble();
+	product->est_fg = query.value("est_fg").toDouble();
+	product->est_abv = query.value("est_abv").toDouble();
+	product->est_color = query.value("est_color").toDouble();
+	product->color_method = query.value("color_method").toInt();
+	product->est_ibu = query.value("est_ibu").toDouble();
+	product->ibu_method = query.value("ibu_method").toInt();
+	product->est_carb = query.value("est_carb").toDouble();
+
+	product->sparge_temp = query.value("sparge_temp").toDouble();
+	product->sparge_ph = query.value("sparge_ph").toDouble();
+	product->sparge_volume = query.value("sparge_volume").toDouble();
+	product->sparge_source = query.value("sparge_source").toInt();
+	product->sparge_acid_type = query.value("sparge_acid_type").toInt();
+	product->sparge_acid_perc = query.value("sparge_acid_perc").toDouble();
+	product->sparge_acid_amount = query.value("sparge_acid_amount").toDouble();
+	product->mash_ph = query.value("mash_ph").toDouble();
+	product->mash_name = query.value("mash_name").toString();
+	product->calc_acid = query.value("calc_acid").toInt() ? true:false;
+
+	product->w1_name = query.value("w1_name").toString();
+	product->w1_amount = query.value("w1_amount").toDouble();
+	product->w1_calcium = query.value("w1_calcium").toDouble();
+	product->w1_sulfate = query.value("w1_sulfate").toDouble();
+	product->w1_chloride = query.value("w1_chloride").toDouble();
+	product->w1_sodium = query.value("w1_sodium").toDouble();
+	product->w1_magnesium = query.value("w1_magnesium").toDouble();
+	product->w1_total_alkalinity = query.value("w1_total_alkalinity").toDouble();
+	product->w1_ph = query.value("w1_ph").toDouble();
+	product->w1_cost = query.value("w1_cost").toDouble();
+	product->w2_name = query.value("w2_name").toString();
+        product->w2_amount = query.value("w2_amount").toDouble();
+        product->w2_calcium = query.value("w2_calcium").toDouble();
+        product->w2_sulfate = query.value("w2_sulfate").toDouble();
+        product->w2_chloride = query.value("w2_chloride").toDouble();
+        product->w2_sodium = query.value("w2_sodium").toDouble();
+        product->w2_magnesium = query.value("w2_magnesium").toDouble();
+        product->w2_total_alkalinity = query.value("w2_total_alkalinity").toDouble();
+        product->w2_ph = query.value("w2_ph").toDouble();
+        product->w2_cost = query.value("w2_cost").toDouble();
+	product->wg_amount = query.value("wg_amount").toDouble();
+        product->wg_calcium = query.value("wg_calcium").toDouble();
+        product->wg_sulfate = query.value("wg_sulfate").toDouble();
+        product->wg_chloride = query.value("wg_chloride").toDouble();
+        product->wg_sodium = query.value("wg_sodium").toDouble();
+        product->wg_magnesium = query.value("wg_magnesium").toDouble();
+        product->wg_total_alkalinity = query.value("wg_total_alkalinity").toDouble();
+        product->wg_ph = query.value("wg_ph").toDouble();
+	product->wb_calcium = query.value("wb_calcium").toDouble();
+        product->wb_sulfate = query.value("wb_sulfate").toDouble();
+        product->wb_chloride = query.value("wb_chloride").toDouble();
+        product->wb_sodium = query.value("wb_sodium").toDouble();
+        product->wb_magnesium = query.value("wb_magnesium").toDouble();
+        product->wb_total_alkalinity = query.value("wb_total_alkalinity").toDouble();
+        product->wb_ph = query.value("wb_ph").toDouble();
+	product->wa_acid_name = query.value("wa_acid_name").toInt();
+        product->wa_acid_perc = query.value("wa_acid_perc").toDouble();
+        product->wa_base_name = query.value("wa_base_name").toInt();
+
+	product->starter_enable = query.value("starter_enable").toInt() ? true:false;
+	product->starter_type = query.value("starter_type").toInt();
+	product->starter_sg = query.value("starter_sg").toDouble();
+	product->starter_viability = query.value("starter_viability").toInt();
+	product->yeast_prod_date = query.value("yeast_prod_date").toDate();
+	product->yeast_pitchrate = query.value("yeast_pitchrate").toDouble();
+	product->prop1_type = query.value("prop1_type").toInt();
+	product->prop1_volume = query.value("prop1_volume").toDouble();
+	product->prop2_type = query.value("prop2_type").toInt();
+        product->prop2_volume = query.value("prop2_volume").toDouble();
+	product->prop3_type = query.value("prop3_type").toInt();
+        product->prop3_volume = query.value("prop3_volume").toDouble();
+	product->prop4_type = query.value("prop4_type").toInt();
+        product->prop4_volume = query.value("prop4_volume").toDouble();
+
+	product->divide_type = query.value("divide_type").toInt();
+	product->divide_size = query.value("divide_size").toDouble();
+	product->divide_factor = query.value("divide_factor").toDouble();
+	product->divide_parts = query.value("divide_parts").toInt();
+	product->divide_part = query.value("divide_part").toInt();
+
+	QJsonParseError parseError;
+        const auto& f_json = query.value("json_fermentables").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()) {
+		double percentcheck = 0;
+		for (int i = 0; i < fermentables.array().size(); i++) {
+		    QJsonObject obj = fermentables.array().at(i).toObject();
+		    //qDebug() << i << obj;
+		    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)
+			product->fermentables_use100 = true;
+		    percentcheck += f.f_percentage;
+		    product->fermentables.append(f);
+		}
+		qDebug() << "fermentables" << product->fermentables.size() << percentcheck;
+		if (percentcheck == 0) {
+		    /* Missing percentages, fix it. */
+		    double total = 0;
+		    for (int i = 0; i < product->fermentables.size(); i++) {
+			if (product->fermentables.at(i).f_added < 4)
+			    total += product->fermentables.at(i).f_amount;
+		    }
+		    for (int i = 0; i < product->fermentables.size(); i++) {
+			if (product->fermentables.at(i).f_added < 4)
+			    product->fermentables[i].f_percentage = round((product->fermentables.at(i).f_amount / total) * 10000.0) / 100.0;
+		    }
+		    qDebug() << " fixed missing percentages";
+		    is_changed();
+		}
+	    }
+        } else {
+	    qDebug() << "empty fermentables";
+	}
+
+	const auto& h_json = query.value("json_hops").toString();
+        if (!h_json.trimmed().isEmpty()) {
+            const auto& formattedJson = QString("%1").arg(h_json);
+            QJsonDocument hops = QJsonDocument::fromJson(formattedJson.toUtf8(), &parseError);
+            if (parseError.error != QJsonParseError::NoError) {
+                qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset;
+	    } else if (hops.isArray()) {
+		for (int i = 0; i < hops.array().size(); i++) {
+		    QJsonObject obj = hops.array().at(i).toObject();
+		    //qDebug() << i << obj;
+		    Hops h;
+		    h.h_name = obj["h_name"].toString();
+		    h.h_origin = obj["h_origin"].toString();
+		    h.h_amount = obj["h_amount"].toDouble();
+		    h.h_cost = obj["h_cost"].toDouble();
+		    h.h_type = obj["h_type"].toInt();
+		    h.h_form = obj["h_form"].toInt();
+		    h.h_useat = obj["h_useat"].toInt();
+		    h.h_time = obj["h_time"].toInt();
+		    h.h_alpha = obj["h_alpha"].toDouble();
+		    h.h_beta = obj["h_beta"].toDouble();
+		    h.h_hsi = obj["h_hsi"].toDouble();
+		    h.h_humulene = obj["h_humulene"].toDouble();
+		    h.h_caryophyllene = obj["h_caryophyllene"].toDouble();
+		    h.h_cohumulone = obj["h_cohumulone"].toDouble();
+		    h.h_myrcene = obj["h_myrcene"].toDouble();
+		    h.h_total_oil = obj["h_total_oil"].toDouble();
+		    product->hops.append(h);
+		}
+		qDebug() << "hops" << product->hops.size();
+	    }
+        } else {
+            qDebug() << "empty hops";
+        }
+
+	const auto& m_json = query.value("json_miscs").toString();
+        if (!m_json.trimmed().isEmpty()) {
+            const auto& formattedJson = QString("%1").arg(m_json);
+            QJsonDocument miscs = QJsonDocument::fromJson(formattedJson.toUtf8(), &parseError);
+            if (parseError.error != QJsonParseError::NoError) {
+                qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset;
+	    } else if (miscs.isArray()) {
+		for (int i = 0; i < miscs.array().size(); i++) {
+		    QJsonObject obj = miscs.array().at(i).toObject();
+		    //qDebug() << i << obj;
+		    Miscs m;
+		    m.m_name = obj["m_name"].toString();
+		    m.m_amount = obj["m_amount"].toDouble();
+		    m.m_type = obj["m_type"].toInt();
+		    m.m_use_use = obj["m_use_use"].toInt();
+		    m.m_time = obj["m_time"].toDouble();
+		    m.m_amount_is_weight = obj["m_amount_is_weight"].toInt() ? true:false;
+		    m.m_cost = obj["m_cost"].toDouble();
+		    product->miscs.append(m);
+		}
+		qDebug() << "miscs" << product->miscs.size();
+	    }
+        } else {
+            qDebug() << "empty miscs";
+        }
+
+	const auto& y_json = query.value("json_yeasts").toString();
+        if (!y_json.trimmed().isEmpty()) {
+            const auto& formattedJson = QString("%1").arg(y_json);
+            QJsonDocument yeasts = QJsonDocument::fromJson(formattedJson.toUtf8(),  &parseError);
+            if (parseError.error != QJsonParseError::NoError) {
+                qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset ;
+	    } else if (yeasts.isArray()) {
+		for (int i = 0; i < yeasts.array().size(); i++) {
+		    QJsonObject obj = yeasts.array().at(i).toObject();
+		    //qDebug() << i << obj;
+		    Yeasts y;
+		    /* First some defaults for newer fields. */
+		    y.y_tolerance = y.y_harvest_time = y.y_pitch_temperature = y.y_zymocide = 0;
+                    y.y_sta1 = y.y_bacteria = y.y_harvest_top = y.y_pofpos = false;
+		    y.y_gr_hl_lo = 50;
+                    y.y_sg_lo = 1.04;
+                    y.y_gr_hl_hi = 80;
+                    y.y_sg_hi = 1.08;
+		    /* Now get what we have got */
+		    y.y_name = obj["y_name"].toString();
+		    y.y_laboratory = obj["y_laboratory"].toString();
+		    y.y_product_id = obj["y_product_id"].toString();
+		    y.y_amount = obj["y_amount"].toDouble();
+		    y.y_type = obj["y_type"].toInt();
+		    y.y_form = obj["y_form"].toInt();
+		    y.y_min_temperature = obj["y_min_temperature"].toDouble();
+		    y.y_max_temperature = obj["y_max_temperature"].toDouble();
+		    y.y_flocculation = obj["y_flocculation"].toInt();
+		    y.y_attenuation = obj["y_attenuation"].toDouble();
+		    y.y_cells = obj["y_cells"].toDouble();
+		    y.y_tolerance = obj["y_tolerance"].toDouble();
+		    y.y_inventory = obj["y_inventory"].toDouble();
+		    y.y_use = obj["y_use"].toInt();
+		    y.y_sta1 = obj["y_sta1"].toInt() ? true:false;
+		    y.y_bacteria = obj["y_bacteria"].toInt() ? true:false;
+		    y.y_harvest_top = obj["y_harvest_top"].toInt() ? true:false;
+		    y.y_harvest_time = obj["y_harvest_time"].toInt();
+		    y.y_pitch_temperature = obj["y_pitch_temperature"].toDouble();
+		    y.y_pofpos = obj["y_pofpos"].toInt() ? true:false;
+		    y.y_zymocide = obj["y_zymocide"].toInt();
+		    y.y_gr_hl_lo = obj["y_gr_hl_lo"].toInt();
+		    y.y_sg_lo = obj["y_sg_lo"].toDouble();
+		    y.y_gr_hl_hi = obj["y_gr_hl_hi"].toInt();
+		    y.y_sg_hi = obj["y_sg_hi"].toDouble();
+		    y.y_cost = obj["y_cost"].toDouble();
+
+		    if (y.y_tolerance == 0 || y.y_cells == 0) {	// More and better tests?
+			/*
+			 * Possible data upgrade needed.
+			 */
+			yquery.prepare("SELECT tolerance,cells,sta1,bacteria,harvest_top,harvest_time,pitch_temperature,"
+				       "pofpos,zymocide,gr_hl_lo,sg_lo,gr_hl_hi,sg_hi "
+				       "FROM inventory_yeasts WHERE name=:name AND laboratory=:laboratory AND product_id=:product_id");
+			yquery.bindValue(":name", y.y_name);
+			yquery.bindValue(":laboratory", y.y_laboratory);
+			yquery.bindValue(":product_id", y.y_product_id);
+            		yquery.exec();
+            		if (yquery.first()) {
+			    y.y_tolerance = yquery.value(0).toDouble();
+			    y.y_cells = yquery.value(1).toDouble();
+			    y.y_sta1 = yquery.value(2).toInt() ? true:false;
+			    y.y_bacteria = yquery.value(3).toInt() ? true:false;
+			    y.y_harvest_top = yquery.value(4).toInt() ? true:false;
+			    y.y_harvest_time = yquery.value(5).toInt();
+			    y.y_pitch_temperature = yquery.value(6).toDouble();
+			    y.y_pofpos = yquery.value(7).toInt() ? true:false;
+			    y.y_zymocide = yquery.value(8).toInt();
+			    y.y_gr_hl_lo = yquery.value(9).toInt();
+			    y.y_sg_lo = yquery.value(10).toDouble();
+			    y.y_gr_hl_hi = yquery.value(11).toInt();
+			    y.y_sg_hi = yquery.value(12).toDouble();
+			} else {
+			    qDebug() << y.y_name << y.y_product_id << "not found for upgrade";
+			}
+		    }
+		    product->yeasts.append(y);
+		}
+		qDebug() << "yeasts" << product->yeasts.size();
+	    }
+        } else {
+            qDebug() << "empty yeasts";
+        }
+
+	const auto& ma_json = query.value("json_mashs").toString().trimmed();
+        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();
+		    product->mashs.append(m);
+		}
+	    }
+        } else {
+            qDebug() << "empty mashs";
+        }
+	qDebug() << "mashs" << product->mashs.size();
+
+    } else {
+	/* Set some defaults */
+	product->locked = false;
+	product->st_name = "";
+	product->st_letter = "";
+	product->st_guide = "";
+	product->st_category = "";
+	product->st_category_number = 0;
+	product->st_type = 0;
+	product->st_og_min = 1.025; product->st_og_max = 1.100;
+	product->st_fg_min = 1.000; product->st_fg_max = 1.020;
+	product->st_ibu_min = 5; product->st_ibu_max = 200;
+	product->st_color_min = 3; product->st_color_max = 100;
+	product->st_carb_min = 1.0; product->st_carb_max = 4.5;
+	product->st_abv_min = 1; product->st_abv_max = 15;
+	product->name = "";
+	product->notes = "";
+	product->efficiency = 75;
+	product->batch_size = 20;
+	product->boil_time = 60;
+	product->boil_size = product->batch_size + (round(product->batch_size * product->boil_time / 60.0) / 10.0);
+	product->type = 2;
+	product->est_og = product->est_fg = product->est_color = product->est_ibu = product->est_abv = 0;
+	product->sparge_temp = 80;
+        product->sparge_ph = 5.4;
+        product->sparge_volume = 8;
+        product->sparge_source = 0;
+        product->sparge_acid_type = 0;
+        product->sparge_acid_perc = 80;
+        product->sparge_acid_amount = 0;
+        product->mash_ph = 5.4;
+        product->mash_name = "";
+        product->calc_acid = true;
+	product->w1_name = "";
+        product->w1_amount = 0;
+        product->w1_calcium = 0;
+        product->w1_sulfate = 0;
+        product->w1_chloride = 0;
+        product->w1_sodium = 0;
+        product->w1_magnesium = 0;
+        product->w1_total_alkalinity = 0;
+        product->w1_ph = 7;
+        product->w1_cost = 0;
+        product->w2_name = "";
+        product->w2_amount = 0;
+        product->w2_calcium = 0;
+        product->w2_sulfate = 0;
+        product->w2_chloride = 0;
+        product->w2_sodium = 0;
+        product->w2_magnesium = 0;
+        product->w2_total_alkalinity = 0;
+        product->w2_ph = 7;
+        product->w2_cost = 0;
+        product->wg_amount = 0;
+        product->wg_calcium = 0;
+        product->wg_sulfate = 0;
+        product->wg_chloride = 0;
+        product->wg_sodium = 0;
+        product->wg_magnesium = 0;
+        product->wg_total_alkalinity = 0;
+        product->wg_ph = 7;
+        product->wb_calcium = 0;
+        product->wb_sulfate = 0;
+        product->wb_chloride = 0;
+        product->wb_sodium = 0;
+        product->wb_magnesium = 0;
+        product->wb_total_alkalinity = 0;
+        product->wb_ph = 7;
+	product->wa_acid_name = 0;
+	product->wa_acid_perc = 80;
+	product->wa_base_name = 0;
+    }
+
+    // Tab generic.
+    ui->lockedEdit->setChecked(product->locked);
+    ui->st_nameEdit->setText(product->st_name);
+    ui->st_groupEdit->setText(product->st_letter);
+    ui->st_guideEdit->setText(product->st_guide);
+    ui->st_catEdit->setText(product->st_category);
+    ui->st_catnrEdit->setText(QString("%1").arg(product->st_category_number));
+    ui->st_typeEdit->setText(style_types[product->st_type]);
+    ui->nameEdit->setText(product->name);
+    ui->notesEdit->setPlainText(product->notes);
+    ui->typeEdit->setCurrentIndex(product->type);
+    ui->batch_sizeEdit->setValue(product->batch_size);
+    ui->boil_sizeEdit->setValue(product->boil_size);
+    ui->boil_timeEdit->setValue(product->boil_time);
+    ui->efficiencyEdit->setValue(product->efficiency);
+    ui->est_ogEdit->setValue(product->est_og);
+    ui->est_ogShow->setRange(product->st_og_min, product->st_og_max);
+    ui->est_ogShow->setPrecision(3);
+    ui->est_ogShow->setMarkerTextIsValue(true);
+    ui->est_ogShow->setValue(product->est_og);
+    ui->est_fgEdit->setValue(product->est_fg);
+    ui->est_fgShow->setRange(product->st_fg_min, product->st_fg_max);
+    ui->est_fgShow->setPrecision(3);
+    ui->est_fgShow->setMarkerTextIsValue(true);
+    ui->est_fgShow->setValue(product->est_fg);
+    ui->est_abvEdit->setValue(product->est_abv);
+    ui->est_abvShow->setRange(product->st_abv_min, product->st_abv_max);
+    ui->est_abvShow->setPrecision(1);
+    ui->est_abvShow->setMarkerTextIsValue(true);
+    ui->est_abvShow->setValue(product->est_abv);
+    ui->est_colorEdit->setValue(product->est_color);
+    ui->est_colorEdit->setStyleSheet(Utils::ebc_to_style(product->est_color));
+    ui->est_colorShow->setPrecision(0);
+    ui->est_colorShow->setMarkerTextIsValue(true);
+    ui->est_colorShow->setRange(product->st_color_min, product->st_color_max);
+    ui->est_colorShow->setValue(product->est_color);
+    ui->color_methodEdit->setCurrentIndex(product->color_method);
+    ui->est_ibuEdit->setValue(product->est_ibu);
+    ui->est_ibuShow->setPrecision(0);
+    ui->est_ibuShow->setMarkerTextIsValue(true);
+    ui->est_ibuShow->setRange(product->st_ibu_min, product->st_ibu_max);
+    ui->est_ibuShow->setValue(product->est_ibu);
+    ui->ibu_methodEdit->setCurrentIndex(product->ibu_method);
+    ui->est_carbEdit->setValue(product->est_carb);
+    ui->est_carbShow->setPrecision(1);
+    ui->est_carbShow->setMarkerTextIsValue(true);
+    ui->est_carbShow->setRange(product->st_carb_min, product->st_carb_max);
+    ui->est_carbShow->setValue(product->est_carb);
+
+    // Tab fermentables.
+    ui->est_og2Edit->setValue(product->est_og);
+    ui->est_color2Edit->setValue(product->est_color);
+    ui->est_color2Edit->setStyleSheet(Utils::ebc_to_style(product->est_color));
+
+    // Tab hops.
+    ui->est_ibu2Edit->setValue(product->est_ibu);
+
+    // Tab yeasts.
+    ui->est_og3Edit->setValue(product->est_og);
+    ui->est_fg3Edit->setValue(product->est_fg);
+    ui->est_abv2Edit->setValue(product->est_abv);
+
+    // Tab mashs.
+    ui->mash_nameEdit->setText(product->mash_name);
+
+    // Tab waters.
+    qDebug() << "water 1" << product->w1_name << "default" << my_default_water;
+    if (product->w1_ph > 4.0) {
+	/*
+	 * Water data seems present, use that and set the name between []
+	 */
+	ui->w1_nameEdit->setPlaceholderText(QString("["+product->w1_name+"]"));
+    } else {
+	bool found = false;
+	if (product->w1_name != "") {
+	    /*
+	     * We have a name, but do we know it?
+	     */
+	    query.prepare("SELECT * FROM inventory_waters WHERE name=:water");
+            query.bindValue(":water", product->w1_name);
+	    query.exec();
+	    found = query.first();
+	}
+	if (!found) {
+	    /*
+	     * Try to load default water
+	     */
+	    query.prepare("SELECT * FROM inventory_waters WHERE record=:record");
+	    query.bindValue(":record", my_default_water);
+	    query.exec();
+	    found = query.first();
+	}
+	if (found) {
+	    product->w1_calcium = query.value(3).toDouble();
+	    product->w1_magnesium = query.value(8).toDouble();
+	    product->w1_total_alkalinity = query.value(11).toDouble();
+	    product->w1_sodium = query.value(7).toDouble();
+	    product->w1_chloride = query.value(6).toDouble();
+	    product->w1_sulfate = query.value(5).toDouble();
+	    product->w1_ph = query.value(9).toDouble();
+            ui->w1_nameEdit->setCurrentIndex(query.value(0).toInt());
+	} else {
+	    product->w1_calcium = 0;
+	    product->w1_magnesium = 0;
+	    product->w1_total_alkalinity = 0;
+	    product->w1_sodium = 0;
+	    product->w1_chloride = 0;
+	    product->w1_sulfate = 0;
+	    product->w1_ph = 0;
+	}
+    }
+    ui->w1_volEdit->setValue(product->w1_amount);
+    ui->w1_caEdit->setValue(product->w1_calcium);
+    ui->w1_mgEdit->setValue(product->w1_magnesium);
+    ui->w1_hco3Edit->setValue(product->w1_total_alkalinity * 1.22);
+    ui->w1_caco3Edit->setValue(product->w1_total_alkalinity);
+    ui->w1_naEdit->setValue(product->w1_sodium);
+    ui->w1_clEdit->setValue(product->w1_chloride);
+    ui->w1_so4Edit->setValue(product->w1_sulfate);
+    ui->w1_phEdit->setValue(product->w1_ph);
+
+    qDebug() << "water 2" << product->w2_name;
+    if (product->w2_ph  > 4.0) {
+	ui->w2_nameEdit->setPlaceholderText(QString("["+product->w2_name+"]"));
+    } else if (product->w2_name != "") {
+	query.prepare("SELECT * FROM inventory_waters WHERE name=:water");
+	query.bindValue(":water", product->w2_name);
+	query.exec();
+	if (query.first()) {
+	    product->w2_calcium = query.value(3).toDouble();
+            product->w2_magnesium = query.value(8).toDouble();
+            product->w2_total_alkalinity = query.value(11).toDouble();
+            product->w2_sodium = query.value(7).toDouble();
+            product->w2_chloride = query.value(6).toDouble();
+            product->w2_sulfate = query.value(5).toDouble();
+            product->w2_ph = query.value(9).toDouble();
+	    ui->w2_nameEdit->setCurrentIndex(query.value(0).toInt());
+	} else {
+	    product->w2_calcium = 0;
+            product->w2_magnesium = 0;
+            product->w2_total_alkalinity = 0;
+            product->w2_sodium = 0;
+            product->w2_chloride = 0;
+            product->w2_sulfate = 0;
+            product->w2_ph = 0;
+	}
+    }
+    ui->w2_volEdit->setValue(product->w2_amount);
+    ui->w2_caEdit->setValue(product->w2_calcium);
+    ui->w2_mgEdit->setValue(product->w2_magnesium);
+    ui->w2_hco3Edit->setValue(product->w2_total_alkalinity * 1.22);
+    ui->w2_caco3Edit->setValue(product->w2_total_alkalinity);
+    ui->w2_naEdit->setValue(product->w2_sodium);
+    ui->w2_clEdit->setValue(product->w2_chloride);
+    ui->w2_so4Edit->setValue(product->w2_sulfate);
+    ui->w2_phEdit->setValue(product->w2_ph);
+    ui->mw_autoEdit->setChecked(product->calc_acid);
+    ui->mw_phEdit->setReadOnly(! product->calc_acid);
+    ui->mw_phEdit->setButtonSymbols(product->calc_acid ? QAbstractSpinBox::UpDownArrows : QAbstractSpinBox::NoButtons);
+    ui->mw_acidvolEdit->setReadOnly(product->calc_acid);
+    ui->mw_acidvolEdit->setButtonSymbols(product->calc_acid ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+
+    ui->sp_volEdit->setValue(product->sparge_volume);
+    ui->sp_tempEdit->setValue(product->sparge_temp);
+    ui->sp_phEdit->setValue(product->sparge_ph);
+    ui->sp_sourceEdit->setCurrentIndex(product->sparge_source);
+    ui->sp_acidtypeEdit->setCurrentIndex(product->sparge_acid_type);
+    ui->sp_acidpercEdit->setValue(product->sparge_acid_perc);
+    ui->sp_acidvolEdit->setValue(product->sparge_acid_amount);
+
+    // All signals from tab "Generic"
+    connect(ui->lockedEdit, &QCheckBox::stateChanged, this, &EditProduct::is_changed);
+    connect(ui->nameEdit, &QLineEdit::textChanged, this, &EditProduct::name_changed);
+    connect(ui->notesEdit, SIGNAL(textChanged()), this, SLOT(notes_changed()));
+    connect(ui->typeEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::brew_type_changed);
+    connect(ui->batch_sizeEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::batch_size_changed);
+    connect(ui->boil_timeEdit, QOverload<int>::of(&QSpinBox::valueChanged), this, &EditProduct::boil_time_changed);
+    connect(ui->efficiencyEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::efficiency_changed);
+    connect(ui->beerstyleEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::style_changed);
+    connect(ui->est_ogEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::est_og_changed);
+    connect(ui->color_methodEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::colormethod_changed);
+    connect(ui->ibu_methodEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::ibumethod_changed);
+    connect(ui->lockedEdit, &QCheckBox::stateChanged, this, &EditProduct::locked_changed);
+
+    // All signals from tab "Fermentables"
+    ui->fermentablesTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    connect(ui->est_og2Edit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::est_og_changed);
+    connect(ui->perc_mashShow, &QProgressBar::valueChanged, this, &EditProduct::ferment_perc_mash_valueChanged);
+    connect(ui->perc_sugarsShow, &QProgressBar::valueChanged, this, &EditProduct::ferment_perc_sugars_valueChanged);
+    connect(ui->perc_caraShow, &QProgressBar::valueChanged, this, &EditProduct::ferment_perc_cara_valueChanged);
+    connect(ui->lintnerShow, &QProgressBar::valueChanged, this, &EditProduct::ferment_lintner_valueChanged);
+    connect(ui->addFermentable, SIGNAL(clicked()), this, SLOT(addFermentRow_clicked()));
+
+    // All signals from tab "Hops"
+    ui->hopsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    connect(ui->hop_tasteShow, &QProgressBar::valueChanged, this, &EditProduct::hop_Flavour_valueChanged);
+    connect(ui->hop_aromaShow, &QProgressBar::valueChanged, this, &EditProduct::hop_Aroma_valueChanged);
+    connect(ui->addHop, SIGNAL(clicked()), this, SLOT(addHopRow_clicked()));
+
+    // All signals from tab "Miscs"
+    ui->miscsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    connect(ui->addMisc, SIGNAL(clicked()), this, SLOT(addMiscRow_clicked()));
+
+    // All signals from tab "Yeasts"
+    ui->yeastsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    connect(ui->addYeast, SIGNAL(clicked()), this, SLOT(addYeastRow_clicked()));
+
+    // All signals from tab "Mash"
+    ui->mashsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    connect(ui->addMash, SIGNAL(clicked()), this, SLOT(addMashRow_clicked()));
+    connect(ui->mash_pickEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::mash_select_changed);
+    connect(ui->mash_nameEdit, &QLineEdit::textChanged, this, &EditProduct::mash_name_changed);
+
+    // All signals from tab "Water"
+    connect(ui->bs_cacl2Edit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::wb_cacl2_changed);
+    connect(ui->bs_caso4Edit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::wb_caso4_changed);
+    connect(ui->bs_mgso4Edit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::wb_mgso4_changed);
+    connect(ui->bs_naclEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::wb_nacl_changed);
+    connect(ui->bs_mgcl2Edit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::wb_mgcl2_changed);
+    connect(ui->bs_nahco3Edit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::wb_nahco3_changed);
+    connect(ui->bs_caco3Edit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::wb_caco3_changed);
+    connect(ui->mw_autoEdit, &QCheckBox::stateChanged, this, &EditProduct::mw_calc_acid_clicked);
+    connect(ui->mw_phEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::mw_ph_changed);
+    connect(ui->mw_acidvolEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::mw_acid_changed);
+    connect(ui->mw_acidPick, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::mw_type_changed);
+    connect(ui->wt_sourceEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::wt_target_changed);
+    connect(ui->w1_nameEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::w1_name_changed);
+    connect(ui->w2_nameEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::w2_name_changed);
+    connect(ui->w2_volEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::w2_volume_changed);
+    connect(ui->sp_sourceEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::sp_source_changed);
+    connect(ui->sp_acidtypeEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::sp_type_changed);
+    connect(ui->sp_phEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::sp_ph_changed);
+
+    setLocked(product->locked);
+
+    ui->saveButton->setEnabled(false);
+    ui->deleteButton->setEnabled((id >= 0 && ! product->locked) ? true:false);
+
+    emit refreshAll();
+}
+
+
+EditProduct::~EditProduct()
+{
+    qDebug() << "EditProduct done start";
+    delete ui;
+    emit entry_changed();
+    qDebug() << "EditProduct done final";
+}
+
+
+void EditProduct::refreshAll()
+{
+    refreshFermentables();
+    calcFermentables();		/* Must be before Hops */
+    refreshHops();
+    calcIBUs();
+    refreshMiscs();
+    refreshYeasts();
+    calcYeast();
+    calcMash();
+    refreshMashs();
+    refreshWaters();
+    calcWater();
+}
+
+
+/*
+ * Window header, mark any change with '**'
+ */
+void EditProduct::WindowTitle()
+{
+    QString txt;
+
+    if (this->recno < 0) {
+	txt = QString(tr("BMSapp - Add new product"));
+    } else {
+	txt = QString(tr("BMSapp - Edit product %1").arg(this->recno));
+    }
+
+    if (this->textIsChanged) {
+	txt.append((QString(" **")));
+    }
+    setWindowTitle(txt);
+}
+
+
+void EditProduct::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 Product"), tr("Name empty or too short."));
+	return;
+    }
+    if (ui->st_nameEdit->text().length() < 2) {
+        QMessageBox::warning(this, tr("Edit Product"), tr("No beerstyle selected."));
+        return;
+    }
+
+    if (this->textIsChanged) {
+    	if (this->recno == -1) {
+    	    query.prepare("INSERT INTO products 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, est_og=:est_og, est_fg=:est_fg, est_abv=:est_abv, "
+		"est_color=:est_color, color_method=:color_method, est_ibu=:est_ibu, ibu_method=:ibu_method, "
+		"est_carb=:est_carb, sparge_temp=:sparge_temp, sparge_ph=:sparge_ph, "
+		"sparge_volume=:sparge_volume, sparge_source=:sparge_source, sparge_acid_type=:sparge_acid_type, "
+		"sparge_acid_perc=:sparge_acid_perc, sparge_acid_amount=:sparge_acid_amount, mash_ph=:mash_ph, "
+		"mash_name=:mash_name, calc_acid=:calc_acid, "
+		"w1_name=:w1_name, w1_amount=:w1_amount, w1_calcium=:w1_calcium, w1_sulfate=:w1_sulfate, "
+		"w1_chloride=:w1_chloride, w1_sodium=:w1_sodium, w1_magnesium=:w1_magnesium, "
+		"w1_total_alkalinity=:w1_total_alkalinity, w1_ph=:w1_ph, w1_cost=:w1_cost, "
+		"w2_name=:w2_name, w2_amount=:w2_amount, w2_calcium=:w2_calcium, w2_sulfate=:w2_sulfate, "
+                "w2_chloride=:w2_chloride, w2_sodium=:w2_sodium, w2_magnesium=:w2_magnesium, "
+                "w2_total_alkalinity=:w2_total_alkalinity, w2_ph=:w2_ph, w2_cost=:w2_cost, "
+		"wg_amount=:wg_amount, wg_calcium=:wg_calcium, wg_sulfate=:wg_sulfate, "
+                "wg_chloride=:wg_chloride, wg_sodium=:wg_sodium, wg_magnesium=:wg_magnesium, "
+                "wg_total_alkalinity=:wg_total_alkalinity, wg_ph=:wg_ph, "
+		"wb_calcium=:wb_calcium, wb_sulfate=:wb_sulfate, wb_chloride=:wb_chloride, wb_sodium=:wb_sodium, "
+		"wb_magnesium=:wb_magnesium, wb_total_alkalinity=:wb_total_alkalinity, wb_ph=:wb_ph, "
+		"wa_acid_name=:wa_acid_name, wa_acid_perc=:wa_acid_perc, wa_base_name=:wa_base_name, "
+		"json_fermentables=:json_fermentables, json_hops=:json_hops, json_miscs=:json_miscs, "
+		"json_yeasts=:json_yeasts, json_mashs=:json_mashs, uuid=:uuid");
+    	} else {
+	    query.prepare("UPDATE products 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, est_og=:est_og, est_fg=:est_fg, est_abv=:est_abv, "
+                "est_color=:est_color, color_method=:color_method, est_ibu=:est_ibu, ibu_method=:ibu_method, "
+                "est_carb=:est_carb, sparge_temp=:sparge_temp, sparge_ph=:sparge_ph, "
+		"sparge_volume=:sparge_volume, sparge_source=:sparge_source, sparge_acid_type=:sparge_acid_type, "
+                "sparge_acid_perc=:sparge_acid_perc, sparge_acid_amount=:sparge_acid_amount, mash_ph=:mash_ph, "
+                "mash_name=:mash_name, calc_acid=:calc_acid, "
+                "w1_name=:w1_name, w1_amount=:w1_amount, w1_calcium=:w1_calcium, w1_sulfate=:w1_sulfate, "
+                "w1_chloride=:w1_chloride, w1_sodium=:w1_sodium, w1_magnesium=:w1_magnesium, "
+                "w1_total_alkalinity=:w1_total_alkalinity, w1_ph=:w1_ph, w1_cost=:w1_cost, "
+                "w2_name=:w2_name, w2_amount=:w2_amount, w2_calcium=:w2_calcium, w2_sulfate=:w2_sulfate, "
+                "w2_chloride=:w2_chloride, w2_sodium=:w2_sodium, w2_magnesium=:w2_magnesium, "
+                "w2_total_alkalinity=:w2_total_alkalinity, w2_ph=:w2_ph, w2_cost=:w2_cost, "
+                "wg_amount=:wg_amount, wg_calcium=:wg_calcium, wg_sulfate=:wg_sulfate, "
+                "wg_chloride=:wg_chloride, wg_sodium=:wg_sodium, wg_magnesium=:wg_magnesium, "
+                "wg_total_alkalinity=:wg_total_alkalinity, wg_ph=:wg_ph, "
+                "wb_calcium=:wb_calcium, wb_sulfate=:wb_sulfate, wb_chloride=:wb_chloride, wb_sodium=:wb_sodium, "
+                "wb_magnesium=:wb_magnesium, wb_total_alkalinity=:wb_total_alkalinity, wb_ph=:wb_ph, "
+                "wa_acid_name=:wa_acid_name, wa_acid_perc=:wa_acid_perc, wa_base_name=:wa_base_name, "
+		"json_fermentables=:json_fermentables, json_hops=:json_hops, json_miscs=:json_miscs, "
+                "json_yeasts=:json_yeasts, json_mashs=:json_mashs WHERE record = :recno");
+    	}
+	query.bindValue(":locked", product->locked ? 1:0);
+	query.bindValue(":st_name", product->st_name);
+	query.bindValue(":st_letter", product->st_letter);
+	query.bindValue(":st_guide", product->st_guide);
+	query.bindValue(":st_category", product->st_category);
+	query.bindValue(":st_catnr", product->st_category_number);
+	query.bindValue(":st_type", product->st_type);
+	query.bindValue(":st_og_min", round(product->st_og_min * 1000) / 1000);
+	query.bindValue(":st_og_max", round(product->st_og_max * 1000) / 1000);
+	query.bindValue(":st_fg_min", round(product->st_fg_min * 1000) / 1000);
+        query.bindValue(":st_fg_max", round(product->st_fg_max * 1000) / 1000);
+	query.bindValue(":st_ibu_min", round(product->st_ibu_min * 10) / 10);
+        query.bindValue(":st_ibu_max", round(product->st_ibu_max * 10) / 10);
+	query.bindValue(":st_color_min", round(product->st_color_min * 10) / 10);
+        query.bindValue(":st_color_max", round(product->st_color_max * 10) / 10);
+	query.bindValue(":st_carb_min", round(product->st_carb_min * 10) / 10);
+        query.bindValue(":st_carb_max", round(product->st_carb_max * 10) / 10);
+	query.bindValue(":st_abv_min", round(product->st_abv_min * 10) / 10);
+        query.bindValue(":st_abv_max", round(product->st_abv_max * 10) / 10);
+	query.bindValue(":name", product->name);
+	query.bindValue(":notes", product->notes);
+	query.bindValue(":type", product->type);
+	query.bindValue(":batch_size", round(product->batch_size * 10) / 10);
+	query.bindValue(":boil_size", round(product->boil_size * 10) / 10);
+	query.bindValue(":boil_time", round(product->boil_time * 10) / 10);
+	query.bindValue(":efficiency", round(product->efficiency * 10) / 10);
+	query.bindValue(":est_og", round(product->est_og * 1000) / 1000);
+	query.bindValue(":est_fg", round(product->est_fg * 1000) / 1000);
+	query.bindValue(":est_abv", round(product->est_abv * 10) / 10);
+	query.bindValue(":est_color", round(product->est_color * 10) / 10);
+	query.bindValue(":color_method", product->color_method);
+	query.bindValue(":est_ibu", round(product->est_ibu * 10) / 10);
+	query.bindValue(":ibu_method", product->ibu_method);
+	query.bindValue(":est_carb", round(product->est_carb * 10) / 10);
+	query.bindValue(":sparge_temp", round(product->sparge_temp * 10) / 10);
+	query.bindValue(":sparge_ph", round(product->sparge_ph * 100) / 100);
+	query.bindValue(":sparge_volume", round(product->sparge_volume * 10) / 10);
+	query.bindValue(":sparge_source", product->sparge_source);
+	query.bindValue(":sparge_acid_type", product->sparge_acid_type);
+	query.bindValue(":sparge_acid_perc", round(product->sparge_acid_perc * 10) / 10);
+	query.bindValue(":sparge_acid_amount", round(product->sparge_acid_amount * 100000) / 100000);
+	query.bindValue(":mash_ph", round(product->mash_ph * 100) / 100);
+	query.bindValue(":mash_name", product->mash_name);
+	query.bindValue(":calc_acid", product->calc_acid ?1:0);
+	query.bindValue(":w1_name", product->w1_name);
+	query.bindValue(":w1_amount", round(product->w1_amount * 10) / 10);
+	query.bindValue(":w1_calcium", round(product->w1_calcium * 100000) / 100000);
+	query.bindValue(":w1_sulfate", round(product->w1_sulfate * 100000) / 100000);
+	query.bindValue(":w1_chloride", round(product->w1_chloride * 100000) / 100000);
+	query.bindValue(":w1_sodium", round(product->w1_sodium * 100000) / 100000);
+	query.bindValue(":w1_magnesium", round(product->w1_magnesium * 100000) / 100000);
+	query.bindValue(":w1_total_alkalinity", round(product->w1_total_alkalinity * 100000) / 100000);
+	query.bindValue(":w1_ph", round(product->w1_ph * 100) / 100);
+	query.bindValue(":w1_cost", round(product->w1_cost * 100) / 100);
+	query.bindValue(":w2_name", product->w2_name);
+        query.bindValue(":w2_amount", round(product->w2_amount * 10) / 10);
+        query.bindValue(":w2_calcium", round(product->w2_calcium * 100000) / 100000);
+        query.bindValue(":w2_sulfate", round(product->w2_sulfate * 100000) / 100000);
+        query.bindValue(":w2_chloride", round(product->w2_chloride * 100000) / 100000);
+        query.bindValue(":w2_sodium", round(product->w2_sodium * 100000) / 100000);
+        query.bindValue(":w2_magnesium", round(product->w2_magnesium * 100000) / 100000);
+        query.bindValue(":w2_total_alkalinity", round(product->w2_total_alkalinity * 100000) / 100000);
+        query.bindValue(":w2_ph", round(product->w2_ph * 100) / 100);
+        query.bindValue(":w2_cost", round(product->w2_cost * 100) / 100);
+	query.bindValue(":wg_amount", round(product->wg_amount * 10) / 10);
+        query.bindValue(":wg_calcium", round(product->wg_calcium * 100000) / 100000);
+        query.bindValue(":wg_sulfate", round(product->wg_sulfate * 100000) / 100000);
+        query.bindValue(":wg_chloride", round(product->wg_chloride * 100000) / 100000);
+        query.bindValue(":wg_sodium", round(product->wg_sodium * 100000) / 100000);
+        query.bindValue(":wg_magnesium", round(product->wg_magnesium * 100000) / 100000);
+        query.bindValue(":wg_total_alkalinity", round(product->wg_total_alkalinity * 100000) / 100000);
+        query.bindValue(":wg_ph", round(product->wg_ph * 100) / 100);
+	query.bindValue(":wb_calcium", round(product->wb_calcium * 100000) / 100000);
+        query.bindValue(":wb_sulfate", round(product->wb_sulfate * 100000) / 100000);
+        query.bindValue(":wb_chloride", round(product->wb_chloride * 100000) / 100000);
+        query.bindValue(":wb_sodium", round(product->wb_sodium * 100000) / 100000);
+        query.bindValue(":wb_magnesium", round(product->wb_magnesium * 100000) / 100000);
+        query.bindValue(":wb_total_alkalinity", round(product->wb_total_alkalinity * 100000) / 100000);
+        query.bindValue(":wb_ph", round(product->wb_ph * 100) / 100);
+	query.bindValue(":wa_acid_name", product->wa_acid_name);
+	query.bindValue(":wa_acid_perc", round(product->wa_acid_perc * 10) / 10);
+	query.bindValue(":wa_base_name", product->wa_base_name);
+
+	if (product->fermentables.size() == 0) {
+	    query.bindValue(":json_fermentables", "[]");
+	} else {
+	    QJsonArray array;
+	    for (int i = 0; i < product->fermentables.size(); i++) {
+		QJsonObject obj;
+		obj.insert("f_name", product->fermentables.at(i).f_name);
+		obj.insert("f_origin", product->fermentables.at(i).f_origin);
+		obj.insert("f_supplier", product->fermentables.at(i).f_supplier);
+		obj.insert("f_amount", round(product->fermentables.at(i).f_amount * 10000) / 10000);
+		obj.insert("f_cost", round(product->fermentables.at(i).f_cost * 1000) / 1000);
+		obj.insert("f_type", product->fermentables.at(i).f_type);
+		obj.insert("f_yield", round(product->fermentables.at(i).f_yield * 10) / 10);
+		obj.insert("f_color", round(product->fermentables.at(i).f_color * 10) / 10);
+		obj.insert("f_coarse_fine_diff", round(product->fermentables.at(i).f_coarse_fine_diff * 10) / 10);
+		obj.insert("f_moisture", round(product->fermentables.at(i).f_moisture * 10) / 10);
+		obj.insert("f_diastatic_power", round(product->fermentables.at(i).f_diastatic_power * 100000) / 100000);
+		obj.insert("f_protein", round(product->fermentables.at(i).f_protein * 10) / 10);
+		obj.insert("f_dissolved_protein", round(product->fermentables.at(i).f_dissolved_protein * 10) / 10);
+		obj.insert("f_max_in_batch", product->fermentables.at(i).f_max_in_batch);
+		obj.insert("f_graintype", product->fermentables.at(i).f_graintype);
+		obj.insert("f_added", product->fermentables.at(i).f_added);
+		obj.insert("f_recommend_mash", product->fermentables.at(i).f_recommend_mash ? 1:0);
+		obj.insert("f_add_after_boil", product->fermentables.at(i).f_add_after_boil ? 1:0);
+		obj.insert("f_adjust_to_total_100", product->fermentables.at(i).f_adjust_to_total_100 ? 1:0);
+		obj.insert("f_percentage", round(product->fermentables.at(i).f_percentage * 10) / 10);
+		obj.insert("f_di_ph", round(product->fermentables.at(i).f_di_ph * 100000) / 100000);
+		obj.insert("f_acid_to_ph_57", round(product->fermentables.at(i).f_acid_to_ph_57 * 100000) / 100000);
+		qDebug() << "fermentables_Json" << i << obj;
+		array.append(obj);      /* Append this object */
+	    }
+	    QJsonDocument doc;
+	    doc.setArray(array);
+//	    qDebug() << doc.toJson(QJsonDocument::Compact);
+	    query.bindValue(":json_fermentables", doc.toJson(QJsonDocument::Compact));
+	}
+
+	if (product->hops.size() == 0) {
+            query.bindValue(":json_hops", "[]");
+        } else {
+            QJsonArray array;
+            for (int i = 0; i < product->hops.size(); i++) {
+                QJsonObject obj;
+		obj.insert("h_name", product->hops.at(i).h_name);
+                obj.insert("h_origin", product->hops.at(i).h_origin);
+		obj.insert("h_amount", round(product->hops.at(i).h_amount * 10000) / 10000);
+		obj.insert("h_cost", round(product->hops.at(i).h_cost * 100) / 100);
+		obj.insert("h_type", product->hops.at(i).h_type);
+		obj.insert("h_form", product->hops.at(i).h_form);
+		obj.insert("h_useat", product->hops.at(i).h_useat);
+		obj.insert("h_time", round(product->hops.at(i).h_time));
+		obj.insert("h_alpha", round(product->hops.at(i).h_alpha * 100) / 100);
+		obj.insert("h_beta", round(product->hops.at(i).h_beta * 100) / 100);
+		obj.insert("h_hsi", round(product->hops.at(i).h_hsi * 100) / 100);
+		obj.insert("h_humulene", round(product->hops.at(i).h_humulene * 100) / 100);
+		obj.insert("h_caryophyllene", round(product->hops.at(i).h_caryophyllene * 100) / 100);
+		obj.insert("h_cohumulone", round(product->hops.at(i).h_cohumulone * 100) / 100);
+		obj.insert("h_myrcene", round(product->hops.at(i).h_myrcene * 100) / 100);
+		obj.insert("h_total_oil", round(product->hops.at(i).h_total_oil * 100) / 100);
+		qDebug() << "hops_Json" << i << obj;
+                array.append(obj);      /* Append this object */
+            }
+	    QJsonDocument doc;
+            doc.setArray(array);
+            query.bindValue(":json_hops", doc.toJson(QJsonDocument::Compact));
+        }
+
+	if (product->miscs.size() == 0) {
+            query.bindValue(":json_miscs", "[]");
+        } else {
+            QJsonArray array;
+            for (int i = 0; i < product->miscs.size(); i++) {
+                QJsonObject obj;
+		obj.insert("m_name", product->miscs.at(i).m_name);
+		obj.insert("m_amount", round(product->miscs.at(i).m_amount * 10000) / 10000);
+		obj.insert("m_type", product->miscs.at(i).m_type);
+		obj.insert("m_use_use", product->miscs.at(i).m_use_use);
+		obj.insert("m_time", round(product->miscs.at(i).m_time));
+		obj.insert("m_amount_is_weight", product->miscs.at(i).m_amount_is_weight ? 1:0);
+		obj.insert("m_cost", round(product->miscs.at(i).m_cost * 10000) / 10000);
+                qDebug() << "miscs_Json" << i << obj;
+                array.append(obj);      /* Append this object */
+            }
+	    QJsonDocument doc;
+            doc.setArray(array);
+//            qDebug() << doc.toJson(QJsonDocument::Compact);
+            query.bindValue(":json_miscs", doc.toJson(QJsonDocument::Compact));
+        }
+
+	if (product->yeasts.size() == 0) {
+            query.bindValue(":json_yeasts", "[]");
+        } else {
+            QJsonArray array;
+            for (int i = 0; i < product->yeasts.size(); i++) {
+                QJsonObject obj;
+		obj.insert("y_name", product->yeasts.at(i).y_name);
+		obj.insert("y_laboratory", product->yeasts.at(i).y_laboratory);
+		obj.insert("y_product_id", product->yeasts.at(i).y_product_id);
+		obj.insert("y_amount", round(product->yeasts.at(i).y_amount * 10000) / 10000);
+		obj.insert("y_type", product->yeasts.at(i).y_type);
+		obj.insert("y_form", product->yeasts.at(i).y_form);
+		obj.insert("y_min_temperature", round(product->yeasts.at(i).y_min_temperature * 10) / 10);
+		obj.insert("y_max_temperature", round(product->yeasts.at(i).y_max_temperature * 10) / 10);
+		obj.insert("y_flocculation", product->yeasts.at(i).y_flocculation);
+		obj.insert("y_attenuation", round(product->yeasts.at(i).y_attenuation * 10) / 10);
+		obj.insert("y_cells", product->yeasts.at(i).y_cells);
+		obj.insert("y_tolerance", round(product->yeasts.at(i).y_tolerance * 10) / 10);
+		obj.insert("y_inventory", round(product->yeasts.at(i).y_inventory * 10000) / 10000);
+		obj.insert("y_use", product->yeasts.at(i).y_use);
+		obj.insert("y_sta1", product->yeasts.at(i).y_sta1 ? 1:0);
+		obj.insert("y_bacteria", product->yeasts.at(i).y_bacteria ? 1:0);
+		obj.insert("y_harvest_top", product->yeasts.at(i).y_harvest_top ? 1:0);
+		obj.insert("y_harvest_time", product->yeasts.at(i).y_harvest_time);
+		obj.insert("y_pitch_temperature", round(product->yeasts.at(i).y_pitch_temperature * 10) / 10);
+		obj.insert("y_pofpos", product->yeasts.at(i).y_pofpos ? 1:0);
+		obj.insert("y_zymocide", product->yeasts.at(i).y_zymocide);
+		obj.insert("y_gr_hl_lo", product->yeasts.at(i).y_gr_hl_lo);
+		obj.insert("y_sg_lo", round(product->yeasts.at(i).y_sg_lo * 1000) / 1000);
+		obj.insert("y_gr_hl_hi", product->yeasts.at(i).y_gr_hl_hi);
+		obj.insert("y_sg_hi", round(product->yeasts.at(i).y_sg_hi * 1000) / 1000);
+		obj.insert("y_cost", round(product->yeasts.at(i).y_cost * 1000) / 1000);
+                qDebug() << "yeasts_Json" << i << obj;
+                array.append(obj);      /* Append this object */
+            }
+	    QJsonDocument doc;
+            doc.setArray(array);
+//            qDebug() << doc.toJson(QJsonDocument::Compact);
+            query.bindValue(":json_yeasts", doc.toJson(QJsonDocument::Compact));
+        }
+
+	if (product->mashs.size() == 0) {
+            query.bindValue(":json_mashs", "[]");
+	    qDebug() << "Saved empty mashs";
+        } else {
+            QJsonArray array;
+            for (int i = 0; i < product->mashs.size(); i++) {
+                QJsonObject obj;
+		obj.insert("step_name", product->mashs.at(i).step_name);
+		obj.insert("step_type", product->mashs.at(i).step_type);
+		obj.insert("step_volume", round(product->mashs.at(i).step_volume * 100) / 100);
+		obj.insert("step_infuse_amount", round(product->mashs.at(i).step_infuse_amount * 100) / 100);
+		obj.insert("step_infuse_temp", round(product->mashs.at(i).step_infuse_temp * 100) / 100);
+		obj.insert("step_temp", round(product->mashs.at(i).step_temp * 100) / 100);
+		obj.insert("step_time", round(product->mashs.at(i).step_time * 100) / 100);
+		obj.insert("ramp_time", round(product->mashs.at(i).ramp_time * 100) / 100);
+		obj.insert("end_temp", round(product->mashs.at(i).end_temp * 100) / 100);
+		obj.insert("step_wg_ratio", round(product->mashs.at(i).step_wg_ratio * 100) / 100);
+                qDebug() << "mashs_Json" << i << obj;
+                array.append(obj);      /* Append this object */
+            }
+	    QJsonDocument doc;
+            doc.setArray(array);
+            qDebug() << doc.toJson(QJsonDocument::Compact);
+            query.bindValue(":json_mashs", doc.toJson(QJsonDocument::Compact));
+        }
+
+	if (this->recno == -1) {
+	    query.bindValue(":uuid", QUuid::createUuid().toString().mid(1, 36));
+	} else {
+	    query.bindValue(":recno", this->recno);
+	}
+	query.exec();
+	qDebug() << query.lastQuery();
+	if (query.lastError().isValid()) {
+	    qDebug() << "EditProduct" << 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 {
+	    /*
+	     * If this was a new product, find out what record number we
+	     * have got and set it. So when the user saves this record
+	     * again, it will be updated instead of inserting a new copy.
+	     */
+	    if (this->recno < 0) {
+		QVariant id = query.lastInsertId();
+		this->recno = product->record = id.toInt();
+		qDebug() << "EditProduct Inserted record" << this->recno;
+	    } else {
+		qDebug() << "EditProduct Updated record" << this->recno;
+	    }
+	}
+    }
+
+    ui->saveButton->setEnabled(false);
+    this->textIsChanged = false;
+    WindowTitle();
+}
+
+
+#include "EditProductTab1.cpp"
+#include "EditProductTab2.cpp"
+#include "EditProductTab3.cpp"
+#include "EditProductTab4.cpp"
+#include "EditProductTab5.cpp"
+#include "EditProductTab6.cpp"
+#include "EditProductTab7.cpp"
+#include "EditProductTab8.cpp"
+#include "EditProductTab9.cpp"
+#include "EditProductTab10.cpp"
+#include "EditProductTab11.cpp"
+#include "EditProductExport.cpp"
+
+
+void EditProduct::on_deleteButton_clicked()
+{
+    QSqlQuery query;
+
+    int rc = QMessageBox::warning(this, tr("Delete product"), tr("Delete %1").arg(product->name),
+                    QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
+    if (rc == QMessageBox::No)
+        return;
+
+    query.prepare("DELETE FROM products WHERE record = :recno");
+    query.bindValue(":recno", this->recno);
+    query.exec();
+    if (query.lastError().isValid()) {
+	qDebug() << "EditProduct" << 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() << "EditProduct Deleted" << this->recno;
+    }
+
+    this->close();
+    this->setResult(1);
+}
+
+
+void EditProduct::is_changed()
+{
+    ui->saveButton->setEnabled(true);
+    ui->deleteButton->setEnabled(((this->recno >= 0) ? true:false) && ! product->locked);
+    this->textIsChanged = true;
+    WindowTitle();
+}
+
+
+void EditProduct::on_quitButton_clicked()
+{
+    if (this->textIsChanged) {
+	int rc = QMessageBox::warning(this, tr("Product changed"), tr("The product 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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EditProduct.h	Thu Apr 28 22:49:13 2022 +0200
@@ -0,0 +1,187 @@
+#ifndef _EDITPRODUCT_H
+#define _EDITPRODUCT_H
+
+#include <QDialog>
+#include <QStringList>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QDoubleSpinBox>
+#include <QCheckBox>
+#include <QComboBox>
+#include <QLineEdit>
+#include <QDialogButtonBox>
+#include <QList>
+#include <QLabel>
+
+#include "global.h"
+
+namespace Ui {
+class EditProduct;
+}
+
+class EditProduct : public QDialog
+{
+    Q_OBJECT
+
+signals:
+    void entry_changed();
+
+public:
+    explicit EditProduct(int id, QWidget *parent = 0);
+    ~EditProduct();
+
+private slots:
+    void on_saveButton_clicked();
+    void on_quitButton_clicked();
+    void on_deleteButton_clicked();
+    void on_exportButton_clicked();
+    void on_printButton_clicked();
+    void is_changed();
+    void name_changed(QString);
+    void notes_changed();
+    void style_changed(int val);
+    void colormethod_changed(int val);
+    void ibumethod_changed(int val);
+    void est_og_changed(double val);
+    void efficiency_changed(double val);
+    void boil_time_changed(int val);
+    void batch_size_changed(double val);
+    void brew_type_changed(int val);
+    void locked_changed(bool val);
+    void refreshFermentables();
+    void refreshHops();
+    void refreshMiscs();
+    void refreshYeasts();
+    void refreshMashs();
+    void refreshWaters();
+    void refreshAll();
+    void ferment_amount_changed(double val);
+    void ferment_pct_changed(double val);
+    void ferment_to100_changed(bool val);
+    void ferment_select_changed(int val);
+    void ferment_instock_changed(bool val);
+    void ferment_added_changed(int val);
+    void hop_amount_changed(double val);
+    void hop_time_changed(int val);
+    void hop_select_changed(int val);
+    void hop_instock_changed(bool val);
+    void hop_useat_changed(int val);
+    void misc_amount_changed(double val);
+    void misc_time_changed(int val);
+    void misc_select_changed(int val);
+    void misc_instock_changed(bool val);
+    void misc_useat_changed(int val);
+    void yeast_amount_changed(double val);
+    void yeast_select_changed(int val);
+    void yeast_instock_changed(bool val);
+    void yeast_useat_changed(int val);
+    void addFermentRow_clicked();
+    void deleteFermentRow_clicked();
+    void editFermentRow_clicked();
+    void addHopRow_clicked();
+    void deleteHopRow_clicked();
+    void editHopRow_clicked();
+    void addMiscRow_clicked();
+    void deleteMiscRow_clicked();
+    void editMiscRow_clicked();
+    void addYeastRow_clicked();
+    void deleteYeastRow_clicked();
+    void editYeastRow_clicked();
+    void addMashRow_clicked();
+    void deleteMashRow_clicked();
+    void editMashRow_clicked();
+    void upMashRow_clicked();
+    void downMashRow_clicked();
+
+    void w1_name_changed(int val);
+    void w2_name_changed(int val);
+    void w2_volume_changed(double val);
+    void wb_cacl2_changed(double val);
+    void wb_caso4_changed(double val);
+    void wb_mgso4_changed(double val);
+    void wb_nacl_changed(double val);
+    void wb_mgcl2_changed(double val);
+    void wb_nahco3_changed(double val);
+    void wb_caco3_changed(double val);
+    void mw_calc_acid_clicked();
+    void wt_target_changed(int val);
+    void mw_ph_changed(double val);
+    void mw_acid_changed(double val);
+    void mw_type_changed(int val);
+    void sp_source_changed(int val);
+    void sp_type_changed(int val);
+    void sp_ph_changed(double val);
+    void step_name_changed(QString val);
+    void step_type_changed(int val);
+    void step_temp_changed(double val);
+    void end_temp_changed(double val);
+    void step_time_changed(double val);
+    void ramp_time_changed(double val);
+    void infuse_changed(double val);
+    void mash_select_changed(int val);
+    void mash_name_changed(QString);
+
+    /* Modified progress bars */
+    void ferment_perc_mash_valueChanged(int value);
+    void ferment_perc_sugars_valueChanged(int value);
+    void ferment_perc_cara_valueChanged(int value);
+    void ferment_lintner_valueChanged(int value);
+    void hop_Flavour_valueChanged(int value);
+    void hop_Aroma_valueChanged(int value);
+
+private:
+    Ui::EditProduct *ui;
+    QString bar_red = "QProgressBar::chunk {background: #FF0000;}";
+    QString bar_orange = "QProgressBar::chunk {background: #EB7331;}";
+    QString bar_green = "QProgressBar::chunk {background: #008C00;}";
+    QString bar_20 = "QProgressBar::chunk {background: #004D00;}";
+    QString bar_40 = "QProgressBar::chunk {background: #008C00;}";
+    QString bar_60 = "QProgressBar::chunk {background: #00BF00;}";
+    QString bar_80 = "QProgressBar::chunk {background: #00FF00;}";
+    QString bar_100 = "QProgressBar::chunk {background: #80FF80;}";
+    int recno;
+    bool textIsChanged = false;
+    /*
+     * Variables for popup ingredients editing.
+     */
+    QComboBox *fselectEdit, *faddedEdit, *hselectEdit,*haddedEdit, *useatEdit, *mselectEdit, *yselectEdit;
+    QLineEdit *fnameEdit, *fsupplierEdit, *hnameEdit, *horiginEdit, *mnameEdit, *ynameEdit, *ylaboratoryEdit, *yproduct_idEdit, *stepnameEdit;
+    QDoubleSpinBox *famountEdit, *pctEdit, *fmaxEdit, *hamountEdit, *ibuEdit, *mamountEdit, *yamountEdit;
+    QDoubleSpinBox *steptempEdit, *endtempEdit, *steptimeEdit, *ramptimeEdit, *stepivolEdit, *stepitmpEdit;
+    QSpinBox *htimeEdit, *mtimeEdit;
+    QCheckBox *to100Edit, *finstockEdit, *hinstockEdit, *minstockEdit, *yinstockEdit;
+    QLabel *htimeLabel, *mtimeLabel, *mamountLabel, *yamountLabel, *ivolLabel, *itmpLabel;
+
+    void to100Fermentables(int row);
+    static bool ferment_sort_test(const Fermentables &D1, const Fermentables &D2);
+    static bool hop_sort_test(const Hops &D1, const Hops &D2);
+    static bool misc_sort_test(const Miscs &D1, const Miscs &D2);
+    static bool yeast_sort_test(const Yeasts &D1, const Yeasts &D2);
+    void WindowTitle();
+    void setLocked(bool val);
+    void brewing_salt_sub(QString salt, double val);
+    void set_brewing_salt(QString salt, double val);
+    void calcFermentables();
+    void calcFermentablesFromOG(double og);
+    void calcIBUs();
+    void adjustHops(double factor);
+    void adjustMiscs(double factor);
+    double ZAlkalinity(double pHZ);
+    double ZRA(double pHZ);
+    double BufferCapacity(Fermentables F);
+    double AcidRequired(double ZpH, Fermentables F);
+    double ProtonDeficit(double pHZ);
+    double MashpH();
+    void calcWater();
+    void calcSparge();
+    double GetBUGU();
+    double GetOptSO4Clratio();
+    void calcYeast();
+    void adjustYeasts(double factor);
+    double infusionVol(double step_infused, double step_mashkg, double infuse_temp, double step_temp, double last_temp);
+    double decoctionVol(double step_volume, double step_temp, double prev_temp);
+    void calcMash();
+    void adjustWaters(double factor);
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EditProductExport.cpp	Thu Apr 28 22:49:13 2022 +0200
@@ -0,0 +1,270 @@
+/**
+ * EditProduct.cpp is part of bmsapp.
+ *
+ * Export product.
+ *
+ * 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/>.
+ */
+
+
+void EditProduct::on_exportButton_clicked()
+{
+    qDebug() << "export";
+
+    const QStringList styletype({ "Lager", "Ale", "Mead", "Wheat", "Mixed", "Cider" });
+    const QStringList producttypes({ "Extract", "Partial Mash", "All Grain" });
+    const QStringList color_method({ "Morey", "Mosher", "Daniels", "Halberstadt", "Naudts" });
+    const QStringList ibu_method({ "Tinseth", "Rager", "Daniels", "Garetz", "Mosher", "Noonan" });
+    const QStringList hop_types({ "Bittering", "Aroma", "Both" });
+    const QStringList hop_forms({ "Pellet", "Plug", "Leaf", "Leaf", "Pellet" });
+    /*                                                    "Leaf Wet", "Cryo"   */
+    /*  We use more hop forms then beerxml knows about, so we send known names */
+    /*  instead of what we internally use. */
+    const QStringList hop_use({ "Mash", "First wort", "Boil", "Aroma", "Whirlpool", "Dry hop" });
+    const QStringList fermentable_type({ "Grain", "Sugar", "Extract", "Dry extract", "Adjunct" });
+    const QStringList fermentable_graintype({ "Base", "Roast", "Crystal", "Kilned", "Sour malt", "Special", "No malt" });
+    const QStringList yeast_type({ "Lager", "Ale", "Wheat", "Wine", "Champagne", "Other", "Other", "Other", "Other", "Other" });
+    const QStringList yeast_form({ "Liquid", "Dry", "Slant", "Culture", "Frozen", "Bottle", "Dry" });
+    const QStringList yeast_use({ "Primary", "Secondary", "Tertiary", "Bottle" });
+    const QStringList misc_type({ "Spice", "Herb", "Flavor", "Fining", "Water agent", "Yeast nutrient", "Other" });
+    const QStringList misc_use({ "Starter", "Mash", "Boil", "Primary", "Secondary", "Bottling" });
+    const QStringList step_type({ "Infusion", "Temperature", "Decoction" });
+
+    QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"), QDir::homePath() + "/" + product->name + ".xml", tr("Files (*.xml)"));
+    if (fileName == 0) {
+        QMessageBox::warning(this, tr("Save File"), tr("No XML file selected."));
+        return;
+    }
+
+    QFile file(fileName);
+    file.open(QIODevice::WriteOnly);
+
+    QXmlStreamWriter *xmlWriter = new QXmlStreamWriter(&file);
+    xmlWriter->writeStartDocument();
+    xmlWriter->setAutoFormatting(true);
+    xmlWriter->setAutoFormattingIndent(1);
+
+    xmlWriter->writeStartElement("RECIPES");
+    xmlWriter->writeStartElement("RECIPE");
+    /*
+     * Product basics
+     */
+    xmlWriter->writeTextElement("VERSION", "1");
+    xmlWriter->writeTextElement("NAME", product->name);
+    if (product->notes != "")
+	xmlWriter->writeTextElement("NOTES", product->notes);
+    xmlWriter->writeTextElement("TYPE", producttypes[product->type]);
+    xmlWriter->writeTextElement("BREWER", "Anonymous");
+    xmlWriter->writeTextElement("BATCH_SIZE", QString::number(product->batch_size, 'f', 4));
+    xmlWriter->writeTextElement("BOIL_SIZE", QString::number(product->boil_size, 'f', 4));
+    xmlWriter->writeTextElement("BOIL_TIME", QString::number(product->boil_time, 'f', 3));
+    xmlWriter->writeTextElement("EFFICIENCY", QString::number(product->efficiency, 'f', 4));
+    xmlWriter->writeTextElement("EST_OG", QString::number(product->est_og, 'f', 3));
+    xmlWriter->writeTextElement("EST_FG", QString::number(product->est_fg, 'f', 3));
+    if (product->est_abv > 0)
+    	xmlWriter->writeTextElement("EST_ABV", QString::number(product->est_abv, 'f', 1));
+    if (product->est_color > 0) {
+	xmlWriter->writeTextElement("EST_COLOR", QString::number(Utils::ebc_to_srm(product->est_color), 'f', 6));
+	xmlWriter->writeTextElement("COLOR_METHOD", color_method[product->color_method]);
+    }
+    if (product->est_ibu > 0) {
+	xmlWriter->writeTextElement("EST_IBU", QString::number(product->est_ibu, 'f', 1));
+	xmlWriter->writeTextElement("IBU_METHOD", ibu_method[product->ibu_method]);
+    }
+
+    xmlWriter->writeStartElement("STYLE");
+	xmlWriter->writeTextElement("VERSION", "1");
+	xmlWriter->writeTextElement("NAME", product->st_name);
+	xmlWriter->writeTextElement("CATEGORY", product->st_category);
+	xmlWriter->writeTextElement("CATEGORY_NUMBER", QString::number(product->st_category_number, 'f', 0));
+	xmlWriter->writeTextElement("STYLE_LETTER", product->st_letter);
+	xmlWriter->writeTextElement("STYLE_GUIDE", product->st_guide);
+	xmlWriter->writeTextElement("TYPE", styletype[product->st_type]);
+	xmlWriter->writeTextElement("OG_MIN", QString::number(product->st_og_min, 'f', 3));
+	xmlWriter->writeTextElement("OG_MAX", QString::number(product->st_og_max, 'f', 3));
+	xmlWriter->writeTextElement("FG_MIN", QString::number(product->st_fg_min, 'f', 3));
+	xmlWriter->writeTextElement("FG_MAX", QString::number(product->st_fg_max, 'f', 3));
+	xmlWriter->writeTextElement("IBU_MIN", QString::number(product->st_ibu_min, 'f', 0));
+	xmlWriter->writeTextElement("IBU_MAX", QString::number(product->st_ibu_max, 'f', 0));
+	xmlWriter->writeTextElement("COLOR_MIN", QString::number(Utils::ebc_to_srm(product->st_color_min), 'f', 2));
+	xmlWriter->writeTextElement("COLOR_MAX", QString::number(Utils::ebc_to_srm(product->st_color_max), 'f', 2));
+	xmlWriter->writeTextElement("CARB_MIN", QString::number(product->st_carb_min, 'f', 1));
+	xmlWriter->writeTextElement("CARB_MAX", QString::number(product->st_carb_max, 'f', 1));
+	xmlWriter->writeTextElement("ABV_MIN", QString::number(product->st_abv_min, 'f', 1));
+	xmlWriter->writeTextElement("ABV_MAX", QString::number(product->st_abv_max, 'f', 1));
+    xmlWriter->writeEndElement();	// STYLE
+
+    xmlWriter->writeStartElement("EQUIPMENT");
+	xmlWriter->writeTextElement("VERSION", "1");
+	xmlWriter->writeTextElement("NAME", "Dummy Brewery");
+	xmlWriter->writeTextElement("BATCH_SIZE", QString::number(product->batch_size, 'f', 2));
+	xmlWriter->writeTextElement("BOIL_SIZE", QString::number(product->boil_size, 'f', 2));
+	xmlWriter->writeTextElement("BOIL_TIME", QString::number(product->boil_time, 'f', 0));
+    xmlWriter->writeEndElement();	// EQUIPMENT
+
+    xmlWriter->writeStartElement("HOPS");
+    for (int i = 0; i < product->hops.size(); i++) {
+	xmlWriter->writeStartElement("HOP");
+	xmlWriter->writeTextElement("VERSION", "1");
+	xmlWriter->writeTextElement("NAME", product->hops.at(i).h_name);
+	xmlWriter->writeTextElement("ALPHA", QString::number(product->hops.at(i).h_alpha, 'f', 1));
+	xmlWriter->writeTextElement("AMOUNT", QString::number(product->hops.at(i).h_amount, 'f', 4));
+	xmlWriter->writeTextElement("USE", hop_use[product->hops.at(i).h_useat]);
+	xmlWriter->writeTextElement("TIME", QString::number(product->hops.at(i).h_time, 'f', 0));
+	xmlWriter->writeTextElement("TYPE", hop_types[product->hops.at(i).h_type]);
+	xmlWriter->writeTextElement("FORM", hop_forms[product->hops.at(i).h_form]);
+	xmlWriter->writeTextElement("BETA", QString::number(product->hops.at(i).h_beta, 'f', 1));
+	xmlWriter->writeTextElement("HSI", QString::number(product->hops.at(i).h_hsi, 'f', 1));
+	xmlWriter->writeTextElement("ORIGIN", product->hops.at(i).h_origin);
+	xmlWriter->writeEndElement();
+    }
+    xmlWriter->writeEndElement();	// HOPS
+
+    xmlWriter->writeStartElement("FERMENTABLES");
+    for (int i = 0; i < product->fermentables.size(); i++) {
+        xmlWriter->writeStartElement("FERMENTABLE");
+        xmlWriter->writeTextElement("VERSION", "1");
+	xmlWriter->writeTextElement("NAME", product->fermentables.at(i).f_name);
+	xmlWriter->writeTextElement("TYPE", fermentable_type[product->fermentables.at(i).f_type]);
+	xmlWriter->writeTextElement("AMOUNT", QString::number(product->fermentables.at(i).f_amount, 'f', 4));
+	xmlWriter->writeTextElement("YIELD", QString::number(product->fermentables.at(i).f_yield, 'f', 1));
+	xmlWriter->writeTextElement("COLOR", QString::number(Utils::ebc_to_srm(product->fermentables.at(i).f_color), 'f', 1));
+	xmlWriter->writeTextElement("ADD_AFTER_BOIL", product->fermentables.at(i).f_add_after_boil ? "TRUE":"FALSE");
+	xmlWriter->writeTextElement("ORIGIN", product->fermentables.at(i).f_origin);
+	xmlWriter->writeTextElement("SUPPLIER", product->fermentables.at(i).f_supplier);
+	if (product->fermentables.at(i).f_coarse_fine_diff)
+	    xmlWriter->writeTextElement("COARSE_FINE_DIFF", QString::number(product->fermentables.at(i).f_coarse_fine_diff, 'f', 4));
+	if (product->fermentables.at(i).f_moisture)
+	    xmlWriter->writeTextElement("MOISTURE", QString::number(product->fermentables.at(i).f_moisture, 'f', 4));
+	if (product->fermentables.at(i).f_diastatic_power)
+	    xmlWriter->writeTextElement("DIASTATIC_POWER", QString::number(product->fermentables.at(i).f_diastatic_power, 'f', 4));
+	if (product->fermentables.at(i).f_protein)
+	    xmlWriter->writeTextElement("PROTEIN", QString::number(product->fermentables.at(i).f_protein, 'f', 4));
+	if (product->fermentables.at(i).f_max_in_batch)
+	    xmlWriter->writeTextElement("MAX_IN_BATCH", QString::number(product->fermentables.at(i).f_max_in_batch, 'f', 1));
+	xmlWriter->writeTextElement("RECOMMEND_MASH", product->fermentables.at(i).f_recommend_mash ? "TRUE":"FALSE");
+	xmlWriter->writeTextElement("GRAINTYPE", fermentable_graintype[product->fermentables.at(i).f_graintype]);
+	xmlWriter->writeEndElement();
+    }
+    xmlWriter->writeEndElement();	// FERMENTABLES
+
+    xmlWriter->writeStartElement("MISCS");
+    for (int i = 0; i < product->miscs.size(); i++) {
+        xmlWriter->writeStartElement("MISC");
+        xmlWriter->writeTextElement("VERSION", "1");
+        xmlWriter->writeTextElement("NAME", product->miscs.at(i).m_name);
+	xmlWriter->writeTextElement("TYPE", misc_type[product->miscs.at(i).m_type]);
+	xmlWriter->writeTextElement("AMOUNT", QString::number(product->miscs.at(i).m_amount, 'f', 5));
+	xmlWriter->writeTextElement("AMOUNT_IS_WEIGHT", product->miscs.at(i).m_amount_is_weight ? "TRUE":"FALSE");
+	xmlWriter->writeTextElement("USE", misc_use[product->miscs.at(i).m_use_use]);
+	xmlWriter->writeTextElement("TIME", QString::number(product->miscs.at(i).m_time, 'f', 0));
+	xmlWriter->writeEndElement();
+    }
+    xmlWriter->writeEndElement();	// MISCS
+
+    xmlWriter->writeStartElement("YEASTS");
+    for (int i = 0; i < product->yeasts.size(); i++) {
+        xmlWriter->writeStartElement("YEAST");
+        xmlWriter->writeTextElement("VERSION", "1");
+        xmlWriter->writeTextElement("NAME", product->yeasts.at(i).y_name);
+	xmlWriter->writeTextElement("TYPE", yeast_type[product->yeasts.at(i).y_type]);
+	xmlWriter->writeTextElement("FORM", yeast_form[product->yeasts.at(i).y_form]);
+	xmlWriter->writeTextElement("AMOUNT", QString::number(product->yeasts.at(i).y_amount, 'f', 5));
+	xmlWriter->writeTextElement("AMOUNT_IS_WEIGHT", (product->yeasts.at(i).y_form == 1) ? "TRUE":"FALSE");
+	xmlWriter->writeTextElement("LABORATORY", product->yeasts.at(i).y_laboratory);
+	xmlWriter->writeTextElement("PRODUCT_ID", product->yeasts.at(i).y_product_id);
+	xmlWriter->writeTextElement("MIN_TEMPERATURE", QString::number(product->yeasts.at(i).y_min_temperature, 'f', 1));
+	xmlWriter->writeTextElement("MAX_TEMPERATURE", QString::number(product->yeasts.at(i).y_max_temperature, 'f', 1));
+	xmlWriter->writeTextElement("ATTENUATION", QString::number(product->yeasts.at(i).y_attenuation, 'f', 1));
+	xmlWriter->writeTextElement("ADD_TO_SECONDARY", (product->yeasts.at(i).y_use == 0) ? "FALSE":"TRUE");
+	xmlWriter->writeEndElement();
+    }
+    xmlWriter->writeEndElement();	// YEASTS
+
+    xmlWriter->writeStartElement("WATERS");
+    if (product->w1_amount > 0) {
+	xmlWriter->writeStartElement("WATER");
+	xmlWriter->writeTextElement("VERSION", "1");
+	xmlWriter->writeTextElement("NAME", product->w1_name);
+	xmlWriter->writeTextElement("AMOUNT", QString::number(product->w1_amount, 'f', 2));
+	xmlWriter->writeTextElement("CALCIUM", QString::number(product->w1_calcium, 'f', 2));
+	xmlWriter->writeTextElement("MAGNESIUM", QString::number(product->w1_magnesium, 'f', 2));
+	xmlWriter->writeTextElement("BICARBONATE", QString::number(product->w1_total_alkalinity * 1.22, 'f', 2));
+	xmlWriter->writeTextElement("SULFATE", QString::number(product->w1_sulfate, 'f', 2));
+	xmlWriter->writeTextElement("CHLORIDE", QString::number(product->w1_chloride, 'f', 2));
+	xmlWriter->writeTextElement("SODIUM", QString::number(product->w1_sodium, 'f', 2));
+	xmlWriter->writeTextElement("PH", QString::number(product->w1_ph, 'f', 2));
+	xmlWriter->writeTextElement("TOTAL_ALKALINITY", QString::number(product->w1_total_alkalinity, 'f', 2));
+	xmlWriter->writeEndElement();
+	if (product->w2_amount > 0) {
+	    xmlWriter->writeStartElement("WATER");
+            xmlWriter->writeTextElement("VERSION", "1");
+            xmlWriter->writeTextElement("NAME", product->w2_name);
+            xmlWriter->writeTextElement("AMOUNT", QString::number(product->w2_amount, 'f', 2));
+            xmlWriter->writeTextElement("CALCIUM", QString::number(product->w2_calcium, 'f', 2));
+            xmlWriter->writeTextElement("MAGNESIUM", QString::number(product->w2_magnesium, 'f', 2));
+            xmlWriter->writeTextElement("BICARBONATE", QString::number(product->w2_total_alkalinity * 1.22, 'f', 2));
+            xmlWriter->writeTextElement("SULFATE", QString::number(product->w2_sulfate, 'f', 2));
+            xmlWriter->writeTextElement("CHLORIDE", QString::number(product->w2_chloride, 'f', 2));
+            xmlWriter->writeTextElement("SODIUM", QString::number(product->w2_sodium, 'f', 2));
+            xmlWriter->writeTextElement("PH", QString::number(product->w2_ph, 'f', 2));
+            xmlWriter->writeTextElement("TOTAL_ALKALINITY", QString::number(product->w2_total_alkalinity, 'f', 2));
+            xmlWriter->writeEndElement();
+	}
+    }
+    xmlWriter->writeEndElement();	// WATERS
+
+    xmlWriter->writeStartElement("MASH");
+    xmlWriter->writeTextElement("VERSION", "1");
+    xmlWriter->writeTextElement("NAME", product->mash_name);
+    xmlWriter->writeTextElement("GRAIN_TEMP", "10.0");
+    xmlWriter->writeTextElement("PH", QString::number(product->sparge_ph, 'f', 2));
+    xmlWriter->writeTextElement("SPARGE_TEMP", QString::number(product->sparge_temp, 'f', 2));
+    xmlWriter->writeStartElement("MASH_STEPS");
+    for (int i = 0; i < product->mashs.size(); i++) {
+	xmlWriter->writeStartElement("MASH_STEP");
+	xmlWriter->writeTextElement("VERSION", "1");
+	xmlWriter->writeTextElement("NAME", product->mashs.at(i).step_name);
+	xmlWriter->writeTextElement("TYPE", step_type[product->mashs.at(i).step_type]);
+	if (product->mashs.at(i).step_type == 0) {
+	    xmlWriter->writeTextElement("INFUSE_AMOUNT", QString::number(product->mashs.at(i).step_infuse_amount, 'f', 3));
+	    xmlWriter->writeTextElement("INFUSE_TEMP", QString::number(product->mashs.at(i).step_infuse_temp, 'f', 3));
+	}
+	if (product->mashs.at(i).step_type == 2) {
+	    xmlWriter->writeTextElement("DECOCTION_AMT", QString::number(product->mashs.at(i).step_infuse_amount, 'f', 3));
+	}
+	xmlWriter->writeTextElement("STEP_TEMP", QString::number(product->mashs.at(i).step_temp, 'f', 1));
+	xmlWriter->writeTextElement("STEP_TIME", QString::number(product->mashs.at(i).step_time, 'f', 1));
+	xmlWriter->writeTextElement("RAMP_TIME", QString::number(product->mashs.at(i).ramp_time, 'f', 1));
+	xmlWriter->writeTextElement("END_TEMP", QString::number(product->mashs.at(i).end_temp, 'f', 1));
+	xmlWriter->writeTextElement("PH", QString::number(product->mash_ph, 'f', 1));
+	xmlWriter->writeEndElement();
+    }
+    xmlWriter->writeEndElement();	// MASH_STEPS
+    xmlWriter->writeEndElement();	// MASH
+    xmlWriter->writeEndElement();	// RECIPE
+    xmlWriter->writeEndElement();	// RECIPES
+    xmlWriter->writeEndDocument();
+    QMessageBox::information(this, tr("Save File"), tr("XML export ready"));
+
+    file.close();
+}
+
+
+void EditProduct::on_printButton_clicked()
+{
+    PrinterDialog(PR_RECIPE, -1, this);
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EditProductTab1.cpp	Thu Apr 28 22:49:13 2022 +0200
@@ -0,0 +1,267 @@
+/**
+ * EditProduct.cpp is part of bmsapp.
+ *
+ * Tab 1, generic settings.
+ *
+ * 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/>.
+ */
+
+
+
+void EditProduct::name_changed(QString name)
+{
+    product->name = name;
+    is_changed();
+}
+
+
+void EditProduct::notes_changed()
+{
+    /* The text cannot be passed in a simple way :) */
+    product->notes = ui->notesEdit->toPlainText();
+    is_changed();
+}
+
+
+/*
+ * New beerstyle is selected.
+ */
+void EditProduct::style_changed(int val)
+{
+    QSqlQuery query;
+
+    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 < (val - 1); i++) {
+        query.next();
+    }
+    // Set relevant fields and update ranges.
+    product->st_name = query.value(1).toString();
+    product->st_category = query.value(2).toString();
+    product->st_category_number = query.value(3).toInt();
+    product->st_letter = query.value(4).toString();
+    product->st_guide = query.value(5).toString();
+    product->st_type = query.value(6).toInt();
+    product->st_og_min = query.value(7).toDouble();
+    product->st_og_max = query.value(8).toDouble();
+    product->st_fg_min = query.value(9).toDouble();
+    product->st_fg_max = query.value(10).toDouble();
+    product->st_ibu_min = query.value(11).toDouble();
+    product->st_ibu_max = query.value(12).toDouble();
+    product->st_color_min = query.value(13).toDouble();
+    product->st_color_max = query.value(14).toDouble();
+    product->st_carb_min = query.value(15).toDouble();
+    product->st_carb_max = query.value(16).toDouble();
+    product->st_abv_min = query.value(17).toDouble();
+    product->st_abv_max = query.value(18).toDouble();
+
+    ui->st_nameEdit->setText(product->st_name);
+    ui->st_groupEdit->setText(product->st_letter);
+    ui->st_guideEdit->setText(product->st_guide);
+    ui->st_catEdit->setText(product->st_category);
+    ui->st_catnrEdit->setText(QString("%1").arg(product->st_category_number));
+    ui->st_typeEdit->setText(style_types[product->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 EditProduct::colormethod_changed(int val)
+{
+    product->color_method = val;
+    calcFermentables();
+    is_changed();
+}
+
+
+void EditProduct::ibumethod_changed(int val)
+{
+    product->ibu_method = val;
+    calcIBUs();
+    is_changed();
+}
+
+
+void EditProduct::est_og_changed(double val)
+{
+    product->est_og = val;
+    calcFermentablesFromOG(val);// Adjust fermentables amounts
+    calcFermentables();		// Update the product details
+    calcIBUs();
+    calcMash();
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::efficiency_changed(double val)
+{
+    double estog = product->est_og;
+    product->efficiency = val;
+    calcFermentablesFromOG(estog);
+    calcFermentables();
+    calcIBUs();
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::boil_time_changed(int val)
+{
+    qDebug() << "boil_time_changed" << val;
+    double new_evap = (0.1 * product->batch_size) * val / 60.0;
+    product->boil_size = product->batch_size + new_evap;
+    product->boil_time = val;
+    ui->boil_sizeEdit->setValue(product->boil_size);
+    calcFermentables();
+    calcIBUs();
+    is_changed();
+}
+
+
+void EditProduct::batch_size_changed(double val)
+{
+    qDebug() << "batch_size_changed" << val << "old" << product->batch_size;
+
+    double evap = (0.1 * val) * product->boil_time / 60.0;
+    product->boil_size = val + evap;
+    double factor = val / product->batch_size;
+    ui->boil_sizeEdit->setValue(product->boil_size);
+    product->sparge_volume *= factor;
+    ui->sp_volEdit->setValue(product->sparge_volume);
+    product->batch_size = val;
+    calcFermentablesFromOG(product->est_og);	// Keep the OG
+    adjustWaters(factor);
+    calcFermentables();
+    adjustHops(factor);
+    adjustMiscs(factor);
+    adjustYeasts(factor);
+    calcIBUs();
+    calcWater();
+    //calcSparge();
+    calcMash();
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::brew_type_changed(int val)
+{
+    product->type;
+    is_changed();
+}
+
+
+void EditProduct::locked_changed(bool val)
+{
+    qDebug() << "locked_changed" << val;
+
+    product->locked = val;
+    setLocked(val);
+    is_changed();
+}
+
+
+void EditProduct::setLocked(bool val)
+{
+    /* Tab 1, generic */
+    ui->typeEdit->setDisabled(val);
+    ui->color_methodEdit->setDisabled(val);
+    ui->ibu_methodEdit->setDisabled(val);
+    ui->beerstyleEdit->setDisabled(val);
+    ui->nameEdit->setReadOnly(val);
+    ui->notesEdit->setReadOnly(val);
+    ui->batch_sizeEdit->setReadOnly(val);
+    ui->batch_sizeEdit->setButtonSymbols(val ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+    ui->boil_sizeEdit->setReadOnly(val);
+    ui->boil_sizeEdit->setButtonSymbols(val ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+    ui->boil_timeEdit->setReadOnly(val);
+    ui->boil_timeEdit->setButtonSymbols(val ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+    ui->efficiencyEdit->setReadOnly(val);
+    ui->efficiencyEdit->setButtonSymbols(val ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+    ui->est_ogEdit->setReadOnly(val);
+    ui->est_ogEdit->setButtonSymbols(val ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+
+    /* Tab 2, fermentables */
+    ui->est_og2Edit->setReadOnly(val);
+    ui->est_og2Edit->setButtonSymbols(val ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+    ui->addFermentable->setEnabled(! val);
+
+    /* Tab 3, hops */
+    ui->addHop->setEnabled(! val);
+
+    /* Tab 4, miscs */
+    ui->addMisc->setEnabled(! val);
+
+    /* Tab 5, yeasts */
+    ui->addYeast->setEnabled(! val);
+
+    /* Tab 6, mash */
+    ui->addMash->setEnabled(! val);
+    ui->mash_nameEdit->setReadOnly(val);
+    ui->mash_pickEdit->setDisabled(val);
+
+    /* Tab 7, water */
+    ui->sp_sourceEdit->setDisabled(val);
+    ui->wt_sourceEdit->setDisabled(val);
+    ui->w1_nameEdit->setDisabled(val);
+    ui->w2_nameEdit->setDisabled(val);
+    ui->mw_acidPick->setDisabled(val);
+    ui->sp_acidtypeEdit->setDisabled(val);
+    ui->w2_volEdit->setReadOnly(val);
+    ui->w2_volEdit->setButtonSymbols(val ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+    ui->sp_phEdit->setReadOnly(val);
+    ui->sp_phEdit->setButtonSymbols(val ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+    ui->sp_tempEdit->setReadOnly(val);
+    ui->sp_tempEdit->setButtonSymbols(val ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+    ui->sp_volEdit->setReadOnly(val);
+    ui->sp_volEdit->setButtonSymbols(val ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+    ui->bs_cacl2Edit->setReadOnly(val);
+    ui->bs_cacl2Edit->setButtonSymbols(val ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+    ui->bs_caso4Edit->setReadOnly(val);
+    ui->bs_caso4Edit->setButtonSymbols(val ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+    ui->bs_mgso4Edit->setReadOnly(val);
+    ui->bs_mgso4Edit->setButtonSymbols(val ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+    ui->bs_naclEdit->setReadOnly(val);
+    ui->bs_naclEdit->setButtonSymbols(val ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+    ui->bs_mgcl2Edit->setReadOnly(val);
+    ui->bs_mgcl2Edit->setButtonSymbols(val ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+    ui->bs_nahco3Edit->setReadOnly(val);
+    ui->bs_nahco3Edit->setButtonSymbols(val ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+    ui->bs_caco3Edit->setReadOnly(val);
+    ui->bs_caco3Edit->setButtonSymbols(val ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+    if (val) {
+    	ui->mw_phEdit->setReadOnly(true);
+	ui->mw_phEdit->setButtonSymbols(QAbstractSpinBox::NoButtons);
+	ui->mw_acidvolEdit->setReadOnly(true);
+	ui->mw_acidvolEdit->setButtonSymbols(QAbstractSpinBox::NoButtons);
+    } else {
+    	ui->mw_phEdit->setReadOnly(! product->calc_acid);
+    	ui->mw_phEdit->setButtonSymbols(product->calc_acid ? QAbstractSpinBox::UpDownArrows : QAbstractSpinBox::NoButtons);
+    	ui->mw_acidvolEdit->setReadOnly(product->calc_acid);
+    	ui->mw_acidvolEdit->setButtonSymbols(product->calc_acid ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+    }
+    ui->mw_autoEdit->setDisabled(val);
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EditProductTab10.cpp	Thu Apr 28 22:49:13 2022 +0200
@@ -0,0 +1,20 @@
+/**
+ * EditProduct.cpp is part of bmsapp.
+ *
+ * Tab 2, equipment settings.
+ *
+ * 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/>.
+ */
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EditProductTab11.cpp	Thu Apr 28 22:49:13 2022 +0200
@@ -0,0 +1,20 @@
+/**
+ * EditProduct.cpp is part of bmsapp.
+ *
+ * Tab 11, package
+ *
+ * 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/>.
+ */
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EditProductTab12.cpp	Thu Apr 28 22:49:13 2022 +0200
@@ -0,0 +1,20 @@
+/**
+ * EditProduct.cpp is part of bmsapp.
+ *
+ * Tab 12, taste notes.
+ *
+ * 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/>.
+ */
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EditProductTab2.cpp	Thu Apr 28 22:49:13 2022 +0200
@@ -0,0 +1,20 @@
+/**
+ * EditProduct.cpp is part of bmsapp.
+ *
+ * Tab 2, equipment settings.
+ *
+ * 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/>.
+ */
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EditProductTab3.cpp	Thu Apr 28 22:49:13 2022 +0200
@@ -0,0 +1,967 @@
+/**
+ * EditProduct.cpp is part of bmsapp.
+ *
+ * Tab 3, fermentables
+ *
+ * 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::ferment_sort_test(const Fermentables &D1, const Fermentables &D2)
+{
+    if (D1.f_added > D2.f_added)
+	return false;
+    if (D1.f_added < D2.f_added)
+	return true;
+    return (D1.f_amount >= D2.f_amount) && (D1.f_color < D2.f_color);
+}
+
+
+void EditProduct::to100Fermentables(int row)
+{
+    if (product->fermentables.at(row).f_adjust_to_total_100) {
+	QWidget *pWidget = new QWidget();
+	QLabel *label = new QLabel;
+	label->setPixmap(QPixmap(":icons/silk/tick.png"));
+	QHBoxLayout *pLayout = new QHBoxLayout(pWidget);
+	pLayout->addWidget(label);
+	pLayout->setAlignment(Qt::AlignCenter);
+	pLayout->setContentsMargins(0, 0, 0, 0);
+	pWidget->setLayout(pLayout);
+	ui->fermentablesTable->setCellWidget(row, 9, pWidget);
+    } else {
+	ui->fermentablesTable->removeCellWidget(row, 9);
+    }
+}
+
+
+void EditProduct::refreshFermentables()
+{
+    QString w;
+    QWidget* pWidget;
+    QHBoxLayout* pLayout;
+    QTableWidgetItem *item;
+
+    qDebug() << "refreshFermentables" << product->fermentables.size();
+    std::sort(product->fermentables.begin(), product->fermentables.end(), ferment_sort_test);
+
+    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,  82);     /* 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(product->fermentables.size());
+
+    for (int i = 0; i < product->fermentables.size(); i++) {
+
+	ui->fermentablesTable->setItem(i, 0, new QTableWidgetItem(product->fermentables.at(i).f_supplier));
+	ui->fermentablesTable->setItem(i, 1, new QTableWidgetItem(product->fermentables.at(i).f_name));
+
+        w = QString("%1").arg(product->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(fermentable_types[product->fermentables.at(i).f_type]);
+	item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+	ui->fermentablesTable->setItem(i, 3, item);
+
+        item = new QTableWidgetItem(fermentable_graintypes[product->fermentables.at(i).f_graintype]);
+        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+        ui->fermentablesTable->setItem(i, 4, item);
+
+        item = new QTableWidgetItem(fermentable_added[product->fermentables.at(i).f_added]);
+        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+        ui->fermentablesTable->setItem(i, 5, item);
+
+        item = new QTableWidgetItem(QString("%1%").arg(product->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(product->fermentables.at(i).f_amount, 4, 'f', 3, '0'));
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->fermentablesTable->setItem(i, 7, item);
+
+	if (product->fermentables.at(i).f_added < 4) {
+            item = new QTableWidgetItem(QString("%1%").arg(product->fermentables.at(i).f_percentage, 2, 'f', 1, '0'));
+	} else {
+	    item = new QTableWidgetItem(QString(""));	// Blank for bottling and kegging.
+	}
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->fermentablesTable->setItem(i, 8, item);
+
+	to100Fermentables(i);
+
+	/* 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(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(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);
+    }
+}
+
+
+void EditProduct::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 product lintner
+    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
+
+    qDebug() << "calcFermentables()";
+
+    /*
+     * Get average mashtemp and mashtime from the Mash schedule.
+     * It is possible that the schedule is not (yet) present.
+     */
+    if (product->mashs.size() > 0) {
+	for (i = 0; i < product->mashs.size(); i++) {
+	    if (product->mashs.at(i).step_type == 0)		// Infusion
+		mashinfuse += product->mashs.at(i).step_infuse_amount;
+	    if (product->mashs.at(i).step_temp < 75) {		// Ignore mashout
+		mashtime += product->mashs.at(i).step_time;
+		mashtemp += product->mashs.at(i).step_time * product->mashs.at(i).step_temp;
+	    }
+	}
+	mashtemp = mashtemp / mashtime;
+	mvol = mashinfuse;
+	qDebug() << "  mash time" << mashtime << "temp" << mashtemp << "infuse" << mashinfuse;
+    } else {
+	qDebug() << "  no mash schedule";
+    }
+
+    const QSignalBlocker blocker1(ui->est_ogEdit);
+    const QSignalBlocker blocker2(ui->est_og2Edit);
+
+    if (product->fermentables.size() < 1) {
+	qDebug() << "  no fermentables, return.";
+	product->est_og = 0.980;
+    	ui->est_ogEdit->setValue(0.980);
+    	ui->est_og2Edit->setValue(0.980);
+    	ui->est_og3Edit->setValue(0.980);
+    	ui->est_ogShow->setValue(0.980);
+    	product->est_color = 0;
+    	ui->est_colorEdit->setValue(0);
+    	ui->est_colorEdit->setStyleSheet(Utils::ebc_to_style(0));
+    	ui->est_color2Edit->setValue(0);
+    	ui->est_color2Edit->setStyleSheet(Utils::ebc_to_style(0));
+    	ui->est_colorShow->setValue(0);
+	ui->perc_mashShow->setValue(0);
+	ui->perc_sugarsShow->setValue(0);
+	ui->perc_caraShow->setValue(0);
+	ui->lintnerShow->setValue(0);
+    	product->est_fg = 0.980;
+    	ui->est_fgEdit->setValue(0.980);
+    	ui->est_fg3Edit->setValue(0.980);
+    	ui->est_fgShow->setValue(0.980);
+    	ui->est_abvEdit->setValue(0);
+    	ui->est_abv2Edit->setValue(0);
+    	ui->est_abvShow->setValue(0);
+    	product->est_abv = 0;
+    	ui->calEdit->setValue(0);
+	product->mashs_kg = 0;
+	return;
+    }
+    qDebug() << "  adjust to 100" << product->fermentables_use100;
+
+    product->mashs_kg = 0;
+    for (i = 0; i < product->fermentables.size(); i++) {
+	if (product->fermentables.at(i).f_type == 1 && product->fermentables.at(i).f_added < 4)		// Sugars
+	    psugar += product->fermentables.at(i).f_percentage;
+	if (product->fermentables.at(i).f_graintype == 2 && product->fermentables.at(i).f_added < 4)	// Crystal/Cara
+	    pcara += product->fermentables.at(i).f_percentage;
+	d = product->fermentables.at(i).f_amount * (product->fermentables.at(i).f_yield / 100) * (1 - product->fermentables.at(i).f_moisture / 100);
+	if (product->fermentables.at(i).f_added == 0) {					// Mash
+	    if (mvol > 0) {							// If mash volume is known
+		mvol += product->fermentables.at(i).f_amount * product->fermentables.at(i).f_moisture / 100;
+		s += d;
+	    }
+	    d = ui->efficiencyEdit->value() / 100 * d;
+	    sugarsm += d;
+	    product->mashs_kg += product->fermentables.at(i).f_amount;
+	}
+	if (product->fermentables.at(i).f_added == 0 || product->fermentables.at(i).f_added == 1)		// Mash or boil
+	    sugarsf += d;
+	if (product->fermentables.at(i).f_added == 2 || product->fermentables.at(i).f_added == 3) {		// Fermentation or lagering
+	    x = (product->fermentables.at(i).f_yield / 100) * (1 - product->fermentables.at(i).f_moisture / 100);
+	    addedS += product->fermentables.at(i).f_amount * x;
+	    addedmass += product->fermentables.at(i).f_amount;
+	    vol += (x * sugardensity + (1 - x) * 1) * product->fermentables.at(i).f_amount;
+	}
+	if (product->fermentables.at(i).f_added == 0 &&
+	    (product->fermentables.at(i).f_type == 0 || product->fermentables.at(i).f_type == 4) &&
+	    product->fermentables.at(i).f_color < 50) {
+	    lintner += product->fermentables.at(i).f_diastatic_power * product->fermentables.at(i).f_amount;
+	}
+	if (product->fermentables.at(i).f_added < 4) {
+	    colort += product->fermentables.at(i).f_amount * Utils::ebc_to_srm(product->fermentables.at(i).f_color);
+	    colorh += product->fermentables.at(i).f_amount * product->fermentables.at(i).f_color * Utils::get_kt(product->fermentables.at(i).f_color);
+	    colorn += (product->fermentables.at(i).f_percentage / 100) * product->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, product->batch_size);
+    qDebug() << "  OG" << ui->est_ogEdit->value() << og;
+    product->est_og = og;
+    ui->est_ogEdit->setValue(og);
+    ui->est_og2Edit->setValue(og);
+    ui->est_og3Edit->setValue(og);
+    ui->est_ogShow->setValue(og);
+
+    product->preboil_sg = Utils::estimate_sg(sugarsm, product->boil_size);
+    qDebug() << "  preboil SG" << product->preboil_sg;
+
+    /*
+     * Color of the wort
+     */
+    if (product->color_method == 4) {				// Naudts
+	color = round(((Utils::sg_to_plato(og) / 8.6) * colorn) + (product->boil_time / 60));
+    } else if (product->color_method == 3) {			// Hans Halberstadt
+	double bv = 0.925;					// Beer loss efficiency
+	double sr = 0.95;					// Mash and sparge efficiency
+	color = round((4.46 * bv * sr) / product->batch_size * colorh);
+    } else {
+	double cw = colort / product->batch_size * 8.34436;
+	color = Utils::kw_to_ebc(product->color_method, cw);
+	//qDebug() << "  oud EBC" << color << "new EBC" << Utils::kw_to_newebc(product->color_method, cw) << "SRM" << Utils::kw_to_srm(product->color_method, cw);
+    }
+    qDebug() << "  color" << ui->est_colorEdit->value() << color << product->est_color;
+    product->est_color = 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 products,
+     * so we assume a certain guessed mashtun size.
+     */
+    ui->perc_mashShow->setValue(round(product->mashs_kg / (product->boil_size / 3) * 100));
+    ui->perc_sugarsShow->setValue(round(psugar));
+    ui->perc_caraShow->setValue(round(pcara));
+    if (product->mashs_kg > 0) {
+	qDebug() << "  lintner" << lintner << "  mashkg" << product->mashs_kg << "final" << round(lintner / product->mashs_kg);
+	ui->lintnerShow->setValue(round(lintner / product->mashs_kg));
+    } else {
+	qDebug() << "  lintner N/A";
+	ui->lintnerShow->setValue(0);
+    }
+
+    /*
+     * Calculate the apparant attenuation.
+     */
+    double svg = 0;
+    if (product->yeasts.size() > 0) {
+        for (i = 0; i < product->yeasts.size(); i++) {
+	    if (product->yeasts.at(i).y_use == 0) {		// Used in primary
+		if (product->yeasts.at(i).y_attenuation > svg)
+		    svg = product->yeasts.at(i).y_attenuation;	// Take the highest if multiple yeasts.
+	    }
+	    // TODO: brett or others in secondary.
+	}
+	qDebug() << "  SVG" << svg;
+    }
+    if (svg == 0)
+	svg = 77.0;
+    ui->est_svgEdit->setValue(svg);
+
+    double fg;
+    if (product->mashs_kg > 0 && mashinfuse > 0 && mashtime > 0 && mashtemp > 0)
+	fg = Utils::estimate_fg(psugar, pcara, mashinfuse / product->mashs_kg, mashtime, mashtemp, svg, og);
+    else
+	fg = Utils::estimate_fg(psugar, pcara, 0, 0, 0, svg, og);
+    qDebug() << "  FG" << ui->est_fgEdit->value() << fg;
+    product->est_fg = 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);
+    product->est_abv = 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)));
+
+    // Bottle priming
+    double priming_total = 0;
+    for (i = 0; i < product->fermentables.size(); i++) {
+	if (product->fermentables.at(i).f_added == 4) {
+	    priming_total += ((product->fermentables.at(i).f_yield / 100) * (1 - product->fermentables.at(i).f_moisture / 100)) *
+		    		product->fermentables.at(i).f_amount;
+	    qDebug() << "  priming" << product->fermentables.at(i).f_amount << "total" << priming_total;
+	}
+    }
+    double grl = priming_total * 1000.0 * (1 / product->batch_size);
+    double volco2 = grl * 0.510;
+    qDebug() << "  priming gr/l" << grl << "volco2" << volco2;
+
+    if (volco2 > 0) {
+	product->est_carb = volco2;
+	ui->est_carbEdit->setValue(product->est_carb);
+	ui->est_carbShow->setValue(product->est_carb);
+    }
+}
+
+
+void EditProduct::calcFermentablesFromOG(double og)
+{
+    qDebug() << "calcFermentablesFromOG" << og;
+
+    int    i;
+    double totmass = 0;
+    double tot = 0;
+    double d, amount;
+    double efficiency = product->efficiency;
+    double sug = Utils::sg_to_plato(og) * product->batch_size * og / 100.0;	// total amount of sugars in kg.
+
+    for (i = 0; i < product->fermentables.size(); i++) {
+	if (product->fermentables.at(i).f_added < 4) {
+	    d = product->fermentables.at(i).f_percentage / 100.0 *
+		    (product->fermentables.at(i).f_yield / 100.0) *
+		    (1 - product->fermentables.at(i).f_moisture / 100.0);
+	    if (product->fermentables.at(i).f_added == 0)  // Mash
+		d = efficiency / 100.0 * d;
+	    tot += d;
+	}
+    }
+    if (tot)
+	totmass = round((sug / tot) * 1000.0) / 1000.0;
+
+    if (totmass) {
+	for (i = 0; i < product->fermentables.size(); i++) {
+	    amount = round(product->fermentables.at(i).f_percentage * 10.0 * totmass) / 1000.0;
+	    product->fermentables[i].f_amount = amount;
+	}
+    }
+}
+
+
+void EditProduct::ferment_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 EditProduct::ferment_perc_sugars_valueChanged(int value)
+{
+    if (value < 20)
+	ui->perc_sugarsShow->setStyleSheet(bar_green);
+    else
+	ui->perc_sugarsShow->setStyleSheet(bar_red);
+}
+
+
+void EditProduct::ferment_perc_cara_valueChanged(int value)
+{
+    if (value < 25)
+	ui->perc_caraShow->setStyleSheet(bar_green);
+    else
+	ui->perc_caraShow->setStyleSheet(bar_red);
+}
+
+
+void EditProduct::ferment_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);
+}
+
+
+void EditProduct::addFermentRow_clicked()
+{
+    Fermentables newf;
+
+    qDebug() << "Add fermentable row";
+
+    for (int i = 0; i < product->fermentables.size(); i++) {
+	if (product->fermentables.at(i).f_amount == 0 && product->fermentables.at(i).f_color == 0)
+	    return;	// Add only one at a time.
+    }
+
+    newf.f_name = "Select one";
+    newf.f_origin = "";
+    newf.f_supplier = "";
+    newf.f_amount = 0;
+    newf.f_cost = 0;
+    newf.f_type = 0;
+    newf.f_yield = 0;
+    newf.f_color = 0;
+    newf.f_coarse_fine_diff = 0;
+    newf.f_moisture = 0;
+    newf.f_diastatic_power = 0;
+    newf.f_protein = 0;
+    newf.f_dissolved_protein = 0;
+    newf.f_max_in_batch = 100;
+    newf.f_graintype = 0;
+    newf.f_added = 0;
+    newf.f_recommend_mash = true;
+    newf.f_add_after_boil = false;
+    newf.f_adjust_to_total_100 = false;
+    newf.f_percentage = 0;
+    newf.f_di_ph = 0;
+    newf.f_acid_to_ph_57 = 0;
+
+    product->fermentables.append(newf);
+    emit refreshAll();
+}
+
+
+void EditProduct::deleteFermentRow_clicked()
+{
+    if (product->locked)
+	return;
+
+    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
+    int row = pb->objectName().toInt();
+    qDebug() << "Delete fermentable row" << row << product->fermentables.size();
+
+    if (product->fermentables.size() < 1)
+	return;
+
+    int rc = QMessageBox::warning(this, tr("Delete fermentable"), tr("Delete %1").arg(product->fermentables.at(row).f_name),
+		    QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
+    if (rc == QMessageBox::No)
+	return;
+
+    product->fermentables.removeAt(row);
+
+    /*
+     * Recalculate the percentages on the rows left.
+     */
+    double total = 0;
+    for (int i = 0; i < product->fermentables.size(); i++)
+        if (product->fermentables.at(i).f_added < 4)             // Only before bottle/kegging
+            total += product->fermentables.at(i).f_amount;
+    for (int i = 0; i < product->fermentables.size(); i++)
+        if (product->fermentables.at(i).f_added < 4)
+            product->fermentables[i].f_percentage = product->fermentables.at(i).f_amount / total * 100;
+
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::ferment_amount_changed(double val)
+{
+    QTableWidgetItem *item;
+    double	total = 0, perc;
+
+    if (product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < 4)
+	return;
+
+    qDebug() << "ferment_amount_changed()" << product->fermentables_row << val;
+
+    product->fermentables[product->fermentables_row].f_amount = val;
+    item = new QTableWidgetItem(QString("%1 Kg").arg(val, 4, 'f', 3, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->fermentablesTable->setItem(product->fermentables_row, 7, item);
+
+    for (int i = 0; i < product->fermentables.size(); i++)
+	if (product->fermentables.at(i).f_added < 4)		// Only before bottle/kegging
+	    total += product->fermentables.at(i).f_amount;
+    /*
+     * Recalculate the percentages
+     */
+    for (int i = 0; i < product->fermentables.size(); i++) {
+	if (product->fermentables.at(i).f_added < 4) {
+	    perc = product->fermentables.at(i).f_amount / total * 100;
+	    product->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 == product->fermentables_row)
+	    	this->pctEdit->setValue(perc);
+	}
+    }
+    is_changed();
+}
+
+
+void EditProduct::ferment_pct_changed(double val)
+{
+    QTableWidgetItem *item;
+    double	total = 0, row100 = -1;
+
+    if (! product->fermentables_use100)
+        return;
+
+    qDebug() << "ferment_pct_changed()" << product->fermentables_row << val;
+    /*
+     * Since we have arrived here, adjust_to_100 is active and
+     * this is not the entry to be adjusted to 100.
+     */
+    for (int i = 0; i < product->fermentables.size(); i++) {
+        if (product->fermentables.at(i).f_added < 4)             // Only before bottle/kegging
+            total += product->fermentables.at(i).f_amount;
+	if (product->fermentables.at(i).f_adjust_to_total_100)
+	    row100 = i;
+    }
+    double oldperc = product->fermentables.at(product->fermentables_row).f_percentage;
+    double diffp = val - oldperc;
+    double diffw = (diffp / 100) * total;
+    qDebug() << "row100" << row100 << "total" << total << "diff kg" << diffw << "diff %" << diffp;
+
+    product->fermentables[product->fermentables_row].f_percentage += diffp;
+    product->fermentables[product->fermentables_row].f_amount += diffw;
+    product->fermentables[row100].f_percentage -= diffp;
+    product->fermentables[row100].f_amount -= diffw;
+
+    item = new QTableWidgetItem(QString("%1 Kg").arg(product->fermentables[product->fermentables_row].f_amount, 4, 'f', 3, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->fermentablesTable->setItem(product->fermentables_row, 7, item);
+    this->famountEdit->setValue(product->fermentables[product->fermentables_row].f_amount);
+
+    item = new QTableWidgetItem(QString("%1%").arg(product->fermentables[product->fermentables_row].f_percentage, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->fermentablesTable->setItem(product->fermentables_row, 8, item);
+
+    item = new QTableWidgetItem(QString("%1 Kg").arg(product->fermentables[row100].f_amount, 4, 'f', 3, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->fermentablesTable->setItem(row100, 7, item);
+
+    item = new QTableWidgetItem(QString("%1%").arg(product->fermentables[row100].f_percentage, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->fermentablesTable->setItem(row100, 8, item);
+
+    is_changed();
+}
+
+
+void EditProduct::ferment_to100_changed(bool val)
+{
+    qDebug() << "ferment_to100_changed()" << product->fermentables_row << val << product->fermentables_use100;
+
+    if (product->fermentables.at(product->fermentables_row).f_added >= 4) {
+	const QSignalBlocker blocker1(to100Edit);
+	product->fermentables[product->fermentables_row].f_adjust_to_total_100 = false;
+	to100Edit->setChecked(false);
+	return;
+    }
+
+    /*
+     * Three scenario's.
+     * 1. There is no fermentable selected yet, just mark it.
+     * 2. There is current one is selected, deselect it.
+     * 3. There is another one selected, deselect and select this one.
+     */
+    if (! product->fermentables_use100 && val) {
+	/* Scenario 1. */
+        product->fermentables_use100 = true;
+        product->fermentables[product->fermentables_row].f_adjust_to_total_100 = true;
+	pctEdit->setReadOnly(false);
+	famountEdit->setReadOnly(true);
+    } else if (product->fermentables_use100 && product->fermentables[product->fermentables_row].f_adjust_to_total_100 && ! val) {
+	/* Scenario 2. */
+	product->fermentables[product->fermentables_row].f_adjust_to_total_100 = false;
+	product->fermentables_use100 = false;
+	pctEdit->setReadOnly(true);
+	famountEdit->setReadOnly(false);
+    } else if (product->fermentables_use100 && ! product->fermentables[product->fermentables_row].f_adjust_to_total_100 && val) {
+	/* Scenario 3. */
+	for (int i = 0; i < product->fermentables.size(); i++) {
+	    product->fermentables[i].f_adjust_to_total_100 = false;
+	}
+	product->fermentables[product->fermentables_row].f_adjust_to_total_100 = true;
+    } else {
+	qDebug() << "bug";
+	return;
+    }
+
+    for (int i = 0; i < product->fermentables.size(); i++) {
+	to100Fermentables(i);
+    }
+    is_changed();
+}
+
+
+void EditProduct::ferment_select_changed(int val)
+{
+    QSqlQuery query;
+    bool instock = finstockEdit->isChecked();
+    QString w;
+    QTableWidgetItem *item;
+
+    if (val < 1)
+	return;
+
+    qDebug() << "ferment_select_changed()" << product->fermentables_row << val << instock;
+
+    /*
+     * Search the fermentable pointed by the index and instock flag.
+     */
+    QString sql = "SELECT name,origin,supplier,cost,type,yield,color,coarse_fine_diff,moisture,diastatic_power,protein,dissolved_protein,max_in_batch,"
+	  	  "graintype,recommend_mash,add_after_boil,di_ph,acid_to_ph_57 FROM inventory_fermentables ";
+    if (instock)
+	sql.append("WHERE inventory > 0 ");
+    sql.append("ORDER BY supplier,name");
+    query.prepare(sql);
+    query.exec();
+    query.first();
+    for (int i = 0; i < (val - 1); i++) {
+	query.next();
+    }
+    qDebug() << "found" << query.value(2).toString() << query.value(0).toString();
+
+    /*
+     * Replace the fermentable record contents
+     */
+    product->fermentables[product->fermentables_row].f_name = query.value(0).toString();
+    product->fermentables[product->fermentables_row].f_origin = query.value(1).toString();
+    product->fermentables[product->fermentables_row].f_supplier = query.value(2).toString();
+    product->fermentables[product->fermentables_row].f_cost = query.value(3).toDouble();
+    product->fermentables[product->fermentables_row].f_type = query.value(4).toInt();
+    product->fermentables[product->fermentables_row].f_yield = query.value(5).toDouble();
+    product->fermentables[product->fermentables_row].f_color = query.value(6).toDouble();
+    product->fermentables[product->fermentables_row].f_coarse_fine_diff = query.value(7).toDouble();
+    product->fermentables[product->fermentables_row].f_moisture = query.value(8).toDouble();
+    product->fermentables[product->fermentables_row].f_diastatic_power = query.value(9).toDouble();
+    product->fermentables[product->fermentables_row].f_protein = query.value(10).toDouble();
+    product->fermentables[product->fermentables_row].f_dissolved_protein = query.value(11).toDouble();
+    product->fermentables[product->fermentables_row].f_max_in_batch = query.value(12).toDouble();
+    product->fermentables[product->fermentables_row].f_graintype = query.value(13).toInt();
+    product->fermentables[product->fermentables_row].f_recommend_mash = query.value(14).toInt() ? true:false;
+    product->fermentables[product->fermentables_row].f_add_after_boil = query.value(15).toInt() ? true:false;
+    product->fermentables[product->fermentables_row].f_di_ph = query.value(16).toDouble();
+    product->fermentables[product->fermentables_row].f_acid_to_ph_57 = query.value(17).toDouble();
+
+    /*
+     * Update the visible fields
+     */
+    fnameEdit->setText(product->fermentables.at(product->fermentables_row).f_name);
+    fsupplierEdit->setText(product->fermentables.at(product->fermentables_row).f_supplier);
+    fmaxEdit->setValue(product->fermentables.at(product->fermentables_row).f_max_in_batch);
+
+    ui->fermentablesTable->setItem(product->fermentables_row, 0, new QTableWidgetItem(product->fermentables.at(product->fermentables_row).f_supplier));
+    ui->fermentablesTable->setItem(product->fermentables_row, 1, new QTableWidgetItem(product->fermentables.at(product->fermentables_row).f_name));
+
+    w = QString("%1").arg(product->fermentables.at(product->fermentables_row).f_color, 1, 'f', 0, '0');
+    item = new QTableWidgetItem(w);
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->fermentablesTable->setItem(product->fermentables_row, 2, item);
+
+    item = new QTableWidgetItem(fermentable_types[product->fermentables.at(product->fermentables_row).f_type]);
+    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+    ui->fermentablesTable->setItem(product->fermentables_row, 3, item);
+
+    item = new QTableWidgetItem(fermentable_graintypes[product->fermentables.at(product->fermentables_row).f_graintype]);
+    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+    ui->fermentablesTable->setItem(product->fermentables_row, 4, item);
+
+    item = new QTableWidgetItem(QString("%1%").arg(product->fermentables.at(product->fermentables_row).f_yield, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->fermentablesTable->setItem(product->fermentables_row, 6, item);
+
+    calcFermentables();
+    is_changed();
+}
+
+
+void EditProduct::ferment_instock_changed(bool val)
+{
+    QSqlQuery query;
+
+    qDebug() << "ferment_instock_changed()" << product->fermentables_row << val;
+
+    this->fselectEdit->setCurrentIndex(-1);
+    this->fselectEdit->clear();
+    QString sql = "SELECT supplier,name,color,inventory FROM inventory_fermentables ";
+    if (val)
+	sql.append("WHERE inventory > 0 ");
+    sql.append("ORDER BY supplier,name");
+    query.prepare(sql);
+    query.exec();
+    query.first();
+    this->fselectEdit->addItem("");	// Start with empty value
+    for (int i = 0; i < query.size(); i++) {
+        this->fselectEdit->addItem(query.value(0).toString()+" - "+query.value(1).toString()+" ("+query.value(2).toString()+" EBC) "+
+			QString("%1 kg").arg(query.value(3).toDouble(), 4, 'f', 3, '0'));
+        query.next();
+    }
+}
+
+
+void EditProduct::ferment_added_changed(int val)
+{
+    qDebug() << "ferment_added_changed()" << product->fermentables_row << val;
+
+    product->fermentables[product->fermentables_row].f_added = val;
+    QTableWidgetItem *item = new QTableWidgetItem(fermentable_added[val]);
+    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+    ui->fermentablesTable->setItem(product->fermentables_row, 5, item);
+
+    famountEdit->setReadOnly(product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < 4);
+    pctEdit->setReadOnly(! (product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < 4));
+
+    double total = 0;
+    for (int i = 0; i < product->fermentables.size(); i++)
+        if (product->fermentables.at(i).f_added < 4)             // Only before bottle/kegging
+            total += product->fermentables.at(i).f_amount;
+    for (int i = 0; i < product->fermentables.size(); i++)
+        if (product->fermentables.at(i).f_added < 4)
+            product->fermentables[i].f_percentage = product->fermentables.at(i).f_amount / total * 100;
+
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::editFermentRow_clicked()
+{
+    QSqlQuery query;
+
+    if (product->locked)
+	return;
+
+    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
+    product->fermentables_row = pb->objectName().toInt();
+    qDebug() << "Edit fermentable row" << product->fermentables_row;
+    Fermentables backup = product->fermentables.at(product->fermentables_row);
+
+    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(525, 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);
+
+    fselectEdit = new QComboBox(dialog);
+    fselectEdit->setObjectName(QString::fromUtf8("fselectEdit"));
+    fselectEdit->setGeometry(QRect(160, 70, 371, 23));
+
+    fnameEdit = new QLineEdit(dialog);
+    fnameEdit->setObjectName(QString::fromUtf8("fnameEdit"));
+    fnameEdit->setText(product->fermentables.at(product->fermentables_row).f_name);
+    fnameEdit->setGeometry(QRect(160, 10, 511, 23));
+    fnameEdit->setReadOnly(true);
+    fsupplierEdit = new QLineEdit(dialog);
+    fsupplierEdit->setObjectName(QString::fromUtf8("fsupplierEdit"));
+    fsupplierEdit->setText(product->fermentables.at(product->fermentables_row).f_supplier);
+    fsupplierEdit->setGeometry(QRect(160, 40, 511, 23));
+    fsupplierEdit->setReadOnly(true);
+    famountEdit = new QDoubleSpinBox(dialog);
+    famountEdit->setObjectName(QString::fromUtf8("famountEdit"));
+    famountEdit->setGeometry(QRect(160, 100, 121, 24));
+    famountEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    famountEdit->setAccelerated(true);
+    famountEdit->setDecimals(3);
+    famountEdit->setReadOnly(product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < 4);
+    famountEdit->setMaximum(100000.0);
+    famountEdit->setSingleStep(0.0010);
+    famountEdit->setValue(product->fermentables.at(product->fermentables_row).f_amount);
+
+    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 (product->fermentables_use100 && product->fermentables.at(product->fermentables_row).f_added < 4) {
+    	if (product->fermentables.at(product->fermentables_row).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(product->fermentables.at(product->fermentables_row).f_percentage);
+
+    faddedEdit = new QComboBox(dialog);
+    faddedEdit->setObjectName(QString::fromUtf8("faddedEdit"));
+    faddedEdit->setGeometry(QRect(160, 190, 161, 23));
+    faddedEdit->addItem(tr("Mash"));
+    faddedEdit->addItem(tr("Boil"));
+    faddedEdit->addItem(tr("Fermentation"));
+    faddedEdit->addItem(tr("Lagering"));
+    faddedEdit->addItem(tr("Bottle"));
+    faddedEdit->addItem(tr("Kegs"));
+    faddedEdit->setCurrentIndex(product->fermentables.at(product->fermentables_row).f_added);
+
+    to100Edit = new QCheckBox(dialog);
+    to100Edit->setObjectName(QString::fromUtf8("to100Edit"));
+    to100Edit->setGeometry(QRect(160, 160, 85, 21));
+    to100Edit->setChecked(product->fermentables.at(product->fermentables_row).f_adjust_to_total_100);
+
+    finstockEdit = new QCheckBox(dialog);
+    finstockEdit->setObjectName(QString::fromUtf8("instockEdit"));
+    finstockEdit->setGeometry(QRect(655, 70, 85, 21));
+    finstockEdit->setChecked(true);
+
+    fmaxEdit = new QDoubleSpinBox(dialog);
+    fmaxEdit->setObjectName(QString::fromUtf8("fmaxEdit"));
+    fmaxEdit->setGeometry(QRect(550, 130, 121, 24));
+    fmaxEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    fmaxEdit->setReadOnly(true);
+    fmaxEdit->setButtonSymbols(QAbstractSpinBox::NoButtons);
+    fmaxEdit->setDecimals(1);
+    fmaxEdit->setValue(product->fermentables.at(product->fermentables_row).f_max_in_batch);
+
+    ferment_instock_changed(true);
+
+    connect(fselectEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::ferment_select_changed);
+    connect(famountEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::ferment_amount_changed);
+    connect(pctEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::ferment_pct_changed);
+    connect(faddedEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::ferment_added_changed);
+    connect(to100Edit, &QCheckBox::stateChanged, this, &EditProduct::ferment_to100_changed);
+    connect(finstockEdit, &QCheckBox::stateChanged, this, &EditProduct::ferment_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->fermentables[product->fermentables_row] = backup;
+	/*
+	 * Recalculate the percentages
+	 */
+	double total = 0;
+	for (int i = 0; i < product->fermentables.size(); i++)
+	    if (product->fermentables.at(i).f_added < 4)             // Only before bottle/kegging
+		total += product->fermentables.at(i).f_amount;
+	product->fermentables_use100 = false;
+	for (int i = 0; i < product->fermentables.size(); i++) {
+	    if (product->fermentables.at(i).f_adjust_to_total_100)
+	    	product->fermentables_use100 = true;
+	    if (product->fermentables.at(i).f_added < 4) {
+		product->fermentables[i].f_percentage = product->fermentables.at(i).f_amount / total * 100;
+	    }
+	}
+    }
+
+    disconnect(fselectEdit, nullptr, nullptr, nullptr);
+    disconnect(famountEdit, nullptr, nullptr, nullptr);
+    disconnect(pctEdit, nullptr, nullptr, nullptr);
+    disconnect(faddedEdit, nullptr, nullptr, nullptr);
+    disconnect(to100Edit, nullptr, nullptr, nullptr);
+    disconnect(finstockEdit, nullptr, nullptr, nullptr);
+    disconnect(buttonBox, nullptr, nullptr, nullptr);
+
+    emit refreshAll();
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EditProductTab4.cpp	Thu Apr 28 22:49:13 2022 +0200
@@ -0,0 +1,636 @@
+/**
+ * EditProduct.cpp is part of bmsapp.
+ *
+ * Tab 4, hops
+ *
+ * bmsapp is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * bmsapp is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+
+bool EditProduct::hop_sort_test(const Hops &D1, const Hops &D2)
+{
+    if (D1.h_useat > D2.h_useat)
+	return false;
+    if (D1.h_useat < D2.h_useat)
+	return true;
+    /* Same useat moments, test time. */
+    if (D1.h_time < D2.h_time)
+	return false;
+    if (D1.h_time > D2.h_time)
+	return true;
+    /* Finally consider the amounts */
+    return (D1.h_amount > D2.h_amount);
+}
+
+
+void EditProduct::refreshHops()
+{
+    QString w;
+    QWidget* pWidget;
+    QHBoxLayout* pLayout;
+    QTableWidgetItem *item;
+
+    qDebug() << "refreshHops" << product->hops.size();
+    std::sort(product->hops.begin(), product->hops.end(), hop_sort_test);
+
+    const QStringList labels({tr("Origin"), tr("Hop"), tr("Type"), tr("Form"), tr("Alpha"), tr("Use at"), tr("Time"),
+                              tr("IBU"), tr("Amount"), tr("Delete"), tr("Edit") });
+
+    ui->hopsTable->setColumnCount(11);
+    ui->hopsTable->setColumnWidth(0, 150);     /* Origin	*/
+    ui->hopsTable->setColumnWidth(1, 225);     /* Hop		*/
+    ui->hopsTable->setColumnWidth(2,  84);     /* Type		*/
+    ui->hopsTable->setColumnWidth(3,  84);     /* Form          */
+    ui->hopsTable->setColumnWidth(4,  75);     /* Alpha%	*/
+    ui->hopsTable->setColumnWidth(5,  75);     /* Added         */
+    ui->hopsTable->setColumnWidth(6,  75);     /* Time		*/
+    ui->hopsTable->setColumnWidth(7,  60);     /* IBU		*/
+    ui->hopsTable->setColumnWidth(8,  90);     /* Amount	*/
+    ui->hopsTable->setColumnWidth(9,  80);     /* Delete        */
+    ui->hopsTable->setColumnWidth(10, 80);     /* Edit          */
+    ui->hopsTable->setHorizontalHeaderLabels(labels);
+    ui->hopsTable->verticalHeader()->hide();
+    ui->hopsTable->setRowCount(product->hops.size());
+
+    for (int i = 0; i < product->hops.size(); i++) {
+
+	ui->hopsTable->setItem(i, 0, new QTableWidgetItem(product->hops.at(i).h_origin));
+	ui->hopsTable->setItem(i, 1, new QTableWidgetItem(product->hops.at(i).h_name));
+
+	item = new QTableWidgetItem(hop_types[product->hops.at(i).h_type]);
+        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+        ui->hopsTable->setItem(i, 2, item);
+
+	item = new QTableWidgetItem(hop_forms[product->hops.at(i).h_form]);
+        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+        ui->hopsTable->setItem(i, 3, item);
+
+	item = new QTableWidgetItem(QString("%1%").arg(product->hops.at(i).h_alpha, 2, 'f', 1, '0'));
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->hopsTable->setItem(i, 4, item);
+
+	item = new QTableWidgetItem(hop_useat[product->hops.at(i).h_useat]);
+        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+        ui->hopsTable->setItem(i, 5, item);
+
+	if (product->hops.at(i).h_useat == 2 || product->hops.at(i).h_useat == 4) {	// Boil or whirlpool
+	    item = new QTableWidgetItem(QString("%1 min.").arg(product->hops.at(i).h_time, 1, 'f', 0, '0'));
+	} else if (product->hops.at(i).h_useat == 5) {					// Dry-hop
+	    item = new QTableWidgetItem(QString("%1 days.").arg(product->hops.at(i).h_time / 1440, 1, 'f', 0, '0'));
+	} else {
+	    item = new QTableWidgetItem(QString(""));
+	}
+	item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->hopsTable->setItem(i, 6, item);
+
+	double ibu = Utils::toIBU(product->hops.at(i).h_useat, product->hops.at(i).h_form, product->preboil_sg, product->batch_size, product->hops.at(i).h_amount,
+	                   product->hops.at(i).h_time, product->hops.at(i).h_alpha, product->ibu_method, 0, product->hops.at(i).h_time, 0);
+	item = new QTableWidgetItem(QString("%1").arg(ibu, 2, 'f', 1, '0'));
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->hopsTable->setItem(i, 7, item);
+
+	if (product->hops.at(i).h_amount < 1.0) {
+	    item = new QTableWidgetItem(QString("%1 gr").arg(product->hops.at(i).h_amount * 1000.0, 2, 'f', 1, '0'));
+	} else {
+	    item = new QTableWidgetItem(QString("%1 kg").arg(product->hops.at(i).h_amount, 4, 'f', 3, '0'));
+	}
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->hopsTable->setItem(i, 8, item);
+
+	/* Add the Delete row button */
+        pWidget = new QWidget();
+        QPushButton* btn_dele = new QPushButton();
+        btn_dele->setObjectName(QString("%1").arg(i));  /* Send row with the button */
+        btn_dele->setText(tr("Delete"));
+        connect(btn_dele, SIGNAL(clicked()), this, SLOT(deleteHopRow_clicked()));
+        pLayout = new QHBoxLayout(pWidget);
+        pLayout->addWidget(btn_dele);
+        pLayout->setContentsMargins(5, 0, 5, 0);
+        pWidget->setLayout(pLayout);
+        ui->hopsTable->setCellWidget(i, 9, pWidget);
+
+        pWidget = new QWidget();
+        QPushButton* btn_edit = new QPushButton();
+        btn_edit->setObjectName(QString("%1").arg(i));  /* Send row with the button */
+        btn_edit->setText(tr("Edit"));
+        connect(btn_edit, SIGNAL(clicked()), this, SLOT(editHopRow_clicked()));
+        pLayout = new QHBoxLayout(pWidget);
+        pLayout->addWidget(btn_edit);
+        pLayout->setContentsMargins(5, 0, 5, 0);
+        pWidget->setLayout(pLayout);
+        ui->hopsTable->setCellWidget(i, 10, pWidget);
+    }
+}
+
+
+void EditProduct::hop_Flavour_valueChanged(int value)
+{
+    if (value < 20) {
+        ui->hop_tasteShow->setStyleSheet(bar_20);
+	ui->hop_tasteShow->setFormat(tr("Very low"));
+    } else if (value < 40) {
+	ui->hop_tasteShow->setStyleSheet(bar_40);
+	ui->hop_tasteShow->setFormat(tr("Low"));
+    } else if (value < 60) {
+        ui->hop_tasteShow->setStyleSheet(bar_60);
+        ui->hop_tasteShow->setFormat(tr("Moderate"));
+    } else if (value < 80) {
+        ui->hop_tasteShow->setStyleSheet(bar_80);
+        ui->hop_tasteShow->setFormat(tr("High"));
+    } else {
+        ui->hop_tasteShow->setStyleSheet(bar_100);
+	ui->hop_tasteShow->setFormat(tr("Very high"));
+    }
+}
+
+
+void EditProduct::hop_Aroma_valueChanged(int value)
+{
+    if (value < 20) {
+        ui->hop_aromaShow->setStyleSheet(bar_20);
+        ui->hop_aromaShow->setFormat(tr("Very low"));
+    } else if (value < 40) {
+        ui->hop_aromaShow->setStyleSheet(bar_40);
+        ui->hop_aromaShow->setFormat(tr("Low"));
+    } else if (value < 60) {
+        ui->hop_aromaShow->setStyleSheet(bar_60);
+        ui->hop_aromaShow->setFormat(tr("Moderate"));
+    } else if (value < 80) {
+        ui->hop_aromaShow->setStyleSheet(bar_80);
+        ui->hop_aromaShow->setFormat(tr("High"));
+    } else {
+        ui->hop_aromaShow->setStyleSheet(bar_100);
+        ui->hop_aromaShow->setFormat(tr("Very high"));
+    }
+}
+
+
+void EditProduct::calcIBUs()
+{
+    double hop_flavour = 0, hop_aroma = 0, ibus = 0;
+
+    for (int i = 0; i < product->hops.size(); i++) {
+
+	ibus += Utils::toIBU(product->hops.at(i).h_useat, product->hops.at(i).h_form, product->preboil_sg, product->batch_size, product->hops.at(i).h_amount,
+                           product->hops.at(i).h_time, product->hops.at(i).h_alpha, product->ibu_method, 0, product->hops.at(i).h_time, 0);
+	hop_flavour += Utils::hopFlavourContribution(product->hops.at(i).h_time, product->batch_size, product->hops.at(i).h_useat, product->hops.at(i).h_amount);
+        hop_aroma += Utils::hopAromaContribution(product->hops.at(i).h_time, product->batch_size, product->hops.at(i).h_useat, product->hops.at(i).h_amount);
+    }
+
+    hop_flavour = round(hop_flavour * 1000.0 / 5.0) / 10;
+    hop_aroma = round(hop_aroma * 1000.0 / 6.0) / 10;
+    if (hop_flavour > 100)
+        hop_flavour = 100;
+    if (hop_aroma > 100)
+        hop_aroma = 100;
+    qDebug() << "ibu" << product->est_ibu << ibus << "flavour" << hop_flavour << "aroma" << hop_aroma << "method" << product->ibu_method;
+
+    product->est_ibu = ibus;
+    ui->est_ibuEdit->setValue(product->est_ibu);
+    ui->est_ibu2Edit->setValue(product->est_ibu);
+    ui->est_ibuShow->setValue(product->est_ibu);
+    ui->hop_tasteShow->setValue(hop_flavour);
+    ui->hop_aromaShow->setValue(hop_aroma);
+}
+
+
+void EditProduct::addHopRow_clicked()
+{
+    Hops newh;
+
+    qDebug() << "Add hop row";
+
+    for (int i = 0; i < product->hops.size(); i++) {
+        if (product->hops.at(i).h_amount == 0 && product->hops.at(i).h_alpha == 0)
+            return;     // Add only one at a time.
+    }
+
+    newh.h_name = "Select one";
+    newh.h_origin = "";
+    newh.h_amount = 0;
+    newh.h_cost = 0;
+    newh.h_type = 0;
+    newh.h_form = 0;
+    newh.h_useat = 2;
+    newh.h_time = 0;
+    newh.h_alpha = 0;
+    newh.h_beta = 0;
+    newh.h_hsi = 0;
+    newh.h_humulene = 0;
+    newh.h_caryophyllene = 0;
+    newh.h_cohumulone = 0;
+    newh.h_myrcene = 0;
+    newh.h_total_oil = 0;
+
+    product->hops.append(newh);
+    emit refreshAll();
+}
+
+
+void EditProduct::deleteHopRow_clicked()
+{
+    if (product->locked || product->hops.size() < 1)
+	return;
+
+    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
+    int row = pb->objectName().toInt();
+    qDebug() << "Delete hop row" << row << product->hops.size();
+
+    int rc = QMessageBox::warning(this, tr("Delete hop"), tr("Delete %1").arg(product->hops.at(row).h_name),
+                    QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
+    if (rc == QMessageBox::No)
+        return;
+
+    product->hops.removeAt(row);
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::hop_amount_changed(double val)
+{
+    QTableWidgetItem *item;
+
+    qDebug() << "hop_amount_changed()" << product->hops_row << val;
+
+    product->hops[product->hops_row].h_amount = val / 1000.0;
+    item = new QTableWidgetItem(QString("%1 gr").arg(val, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->hopsTable->setItem(product->hops_row, 8, item);
+
+    double ibu = Utils::toIBU(product->hops.at(product->hops_row).h_useat, product->hops.at(product->hops_row).h_form, product->preboil_sg,
+                              product->batch_size, product->hops.at(product->hops_row).h_amount, product->hops.at(product->hops_row).h_time,
+                              product->hops.at(product->hops_row).h_alpha, product->ibu_method, 0, product->hops.at(product->hops_row).h_time, 0);
+
+    ibuEdit->setValue(ibu);
+    item = new QTableWidgetItem(QString("%1").arg(ibu, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->hopsTable->setItem(product->hops_row, 7, item);
+
+    calcIBUs();
+    is_changed();
+}
+
+
+void EditProduct::hop_time_changed(int val)
+{
+    QTableWidgetItem *item;
+
+    qDebug() << "hop_time_changed()" << product->hops_row << val;
+
+    if (product->hops.at(product->hops_row).h_useat == 2 || product->hops.at(product->hops_row).h_useat == 4) {       // Boil or whirlpool
+        item = new QTableWidgetItem(QString("%1 min.").arg(val, 1, 'f', 0, '0'));
+	product->hops[product->hops_row].h_time = val;
+    } else if (product->hops.at(product->hops_row).h_useat == 5) {                                   // Dry-hop
+        item = new QTableWidgetItem(QString("%1 days.").arg(val, 1, 'f', 0, '0'));
+	product->hops[product->hops_row].h_time = val * 1440;
+    } else {
+        item = new QTableWidgetItem(QString(""));
+	product->hops[product->hops_row].h_time = val;
+    }
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->hopsTable->setItem(product->hops_row, 6, item);
+
+    double ibu = Utils::toIBU(product->hops.at(product->hops_row).h_useat, product->hops.at(product->hops_row).h_form, product->preboil_sg,
+                              product->batch_size, product->hops.at(product->hops_row).h_amount, product->hops.at(product->hops_row).h_time,
+ 			      product->hops.at(product->hops_row).h_alpha, product->ibu_method, 0, product->hops.at(product->hops_row).h_time, 0);
+
+    ibuEdit->setValue(ibu);
+    item = new QTableWidgetItem(QString("%1").arg(ibu, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->hopsTable->setItem(product->hops_row, 7, item);
+
+    calcIBUs();
+    is_changed();
+}
+
+
+void EditProduct::hop_select_changed(int val)
+{
+    QSqlQuery query;
+    bool instock = hinstockEdit->isChecked();
+    QString w;
+    QTableWidgetItem *item;
+
+    if (val < 1)
+        return;
+
+    qDebug() << "hop_select_changed()" << product->fermentables_row << val << instock;
+
+    /*
+     * Search the hop pointed by the index and instock flag.
+     */
+    QString sql = "SELECT name,origin,alpha,beta,humulene,caryophyllene,cohumulone,myrcene,hsi,total_oil,type,form,cost FROM inventory_hops ";
+    if (instock)
+        sql.append("WHERE inventory > 0 ");
+    sql.append("ORDER BY origin,name");
+    query.prepare(sql);
+    query.exec();
+    query.first();
+    for (int i = 0; i < (val - 1); i++) {
+        query.next();
+    }
+    qDebug() << "found" << query.value(1).toString() << query.value(0).toString();
+
+    /*
+     * Replace the hop record contents
+     */
+    product->hops[product->hops_row].h_name = query.value(0).toString();
+    product->hops[product->hops_row].h_origin = query.value(1).toString();
+    product->hops[product->hops_row].h_alpha = query.value(2).toDouble();
+    product->hops[product->hops_row].h_beta = query.value(3).toDouble();
+    product->hops[product->hops_row].h_humulene = query.value(4).toDouble();
+    product->hops[product->hops_row].h_caryophyllene = query.value(5).toDouble();
+    product->hops[product->hops_row].h_cohumulone = query.value(6).toDouble();
+    product->hops[product->hops_row].h_myrcene = query.value(7).toDouble();
+    product->hops[product->hops_row].h_hsi = query.value(8).toDouble();
+    product->hops[product->hops_row].h_total_oil = query.value(9).toDouble();
+    product->hops[product->hops_row].h_type = query.value(10).toInt();
+    product->hops[product->hops_row].h_form = query.value(11).toInt();
+    product->hops[product->hops_row].h_cost = query.value(12).toDouble();
+
+    /*
+     * Update the visible fields
+     */
+    hnameEdit->setText(product->hops.at(product->hops_row).h_name);
+    horiginEdit->setText(product->hops.at(product->hops_row).h_origin);
+
+    double ibu = Utils::toIBU(product->hops.at(product->hops_row).h_useat, product->hops.at(product->hops_row).h_form, product->preboil_sg,
+		              product->batch_size, product->hops.at(product->hops_row).h_amount, product->hops.at(product->hops_row).h_time,
+			      product->hops.at(product->hops_row).h_alpha, product->ibu_method, 0, product->hops.at(product->hops_row).h_time, 0);
+    ibuEdit->setValue(ibu);
+
+    ui->hopsTable->setItem(product->hops_row, 0, new QTableWidgetItem(product->hops.at(product->hops_row).h_origin));
+    ui->hopsTable->setItem(product->hops_row, 1, new QTableWidgetItem(product->hops.at(product->hops_row).h_name));
+
+    item = new QTableWidgetItem(hop_types[product->hops.at(product->hops_row).h_type]);
+    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+    ui->hopsTable->setItem(product->hops_row, 2, item);
+
+    item = new QTableWidgetItem(hop_forms[product->hops.at(product->hops_row).h_form]);
+    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+    ui->hopsTable->setItem(product->hops_row, 3, item);
+
+    item = new QTableWidgetItem(QString("%1%").arg(product->hops.at(product->hops_row).h_alpha, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->hopsTable->setItem(product->hops_row, 4, item);
+
+    item = new QTableWidgetItem(QString("%1").arg(ibu, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->hopsTable->setItem(product->hops_row, 7, item);
+
+    calcIBUs();
+    is_changed();
+}
+
+
+void EditProduct::hop_instock_changed(bool val)
+{
+    QSqlQuery query;
+
+    qDebug() << "hop_instock_changed()" << product->hops_row << val;
+
+    this->hselectEdit->setCurrentIndex(-1);
+    this->hselectEdit->clear();
+    QString sql = "SELECT origin,name,alpha,inventory FROM inventory_hops ";
+    if (val)
+        sql.append("WHERE inventory > 0 ");
+    sql.append("ORDER BY origin,name");
+    query.prepare(sql);
+    query.exec();
+    query.first();
+    this->hselectEdit->addItem("");      // Start with empty value
+    for (int i = 0; i < query.size(); i++) {
+        this->hselectEdit->addItem(query.value(0).toString()+" - "+query.value(1).toString()+" ("+query.value(2).toString()+"%) "+
+                        QString("%1 gr").arg(query.value(3).toDouble() * 1000.0, 2, 'f', 1, '0'));
+        query.next();
+    }
+}
+
+
+void EditProduct::hop_useat_changed(int val)
+{
+    qDebug() << "hop_useat_changed()" << product->hops_row << val;
+
+    product->hops[product->hops_row].h_useat = val;
+    QTableWidgetItem *item = new QTableWidgetItem(hop_useat[val]);
+    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+    ui->hopsTable->setItem(product->hops_row, 5, item);
+
+    if (val == 2 || val == 4) {	// Boil or whirlpool
+	htimeLabel->setText(tr("Time in minutes:"));
+        htimeEdit->setValue(product->hops.at(product->hops_row).h_time);
+	htimeEdit->setReadOnly(false);
+    } else if (val == 5) {	// Dry-hop
+        htimeLabel->setText(tr("Time in days:"));
+	htimeEdit->setValue(product->hops.at(product->hops_row).h_time / 1440);
+	htimeEdit->setReadOnly(false);
+    } else {
+        htimeLabel->setText("");
+	htimeEdit->setValue(0);
+	htimeEdit->setReadOnly(true);
+    }
+
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::editHopRow_clicked()
+{
+    QSqlQuery query;
+
+    if (product->locked)
+	return;
+
+    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
+    product->hops_row = pb->objectName().toInt();
+    qDebug() << "Edit hop row" << product->hops_row;
+    Hops backup = product->hops.at(product->hops_row);
+
+    QDialog* dialog = new QDialog(this);
+    dialog->resize(738, 260);
+    QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
+    buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
+    buttonBox->setGeometry(QRect(30, 210, 671, 32));
+    buttonBox->setLayoutDirection(Qt::LeftToRight);
+    buttonBox->setOrientation(Qt::Horizontal);
+    buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
+    buttonBox->setCenterButtons(true);
+    QLabel *nameLabel = new QLabel(dialog);
+    nameLabel->setObjectName(QString::fromUtf8("nameLabel"));
+    nameLabel->setText(tr("Current hop:"));
+    nameLabel->setGeometry(QRect(10, 10, 141, 20));
+    nameLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    QLabel *originLabel = new QLabel(dialog);
+    originLabel->setObjectName(QString::fromUtf8("originLabel"));
+    originLabel->setText(tr("Origin:"));
+    originLabel->setGeometry(QRect(10, 40, 141, 20));
+    originLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    QLabel *amountLabel = new QLabel(dialog);
+    amountLabel->setObjectName(QString::fromUtf8("amountLabel"));
+    amountLabel->setText(tr("Amount in gr:"));
+    amountLabel->setGeometry(QRect(10, 100, 141, 20));
+    amountLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    htimeLabel = new QLabel(dialog);
+    htimeLabel->setObjectName(QString::fromUtf8("htimeLabel"));
+    if (product->hops.at(product->hops_row).h_useat == 5)		// Dry-hop
+	htimeLabel->setText(tr("Time in days:"));
+    else if (product->hops.at(product->hops_row).h_useat == 2 || product->hops.at(product->hops_row).h_useat == 4)	// Boil or whirlpool
+    	htimeLabel->setText(tr("Time in minutes:"));
+    else
+	htimeLabel->setText("");
+
+    htimeLabel->setGeometry(QRect(10, 130, 141, 20));
+    htimeLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    QLabel *useatLabel = new QLabel(dialog);
+    useatLabel->setObjectName(QString::fromUtf8("useatLabel"));
+    useatLabel->setText(tr("Use at:"));
+    useatLabel->setGeometry(QRect(10, 160, 141, 20));
+    useatLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    QLabel *selectLabel = new QLabel(dialog);
+    selectLabel->setObjectName(QString::fromUtf8("selectLabel"));
+    selectLabel->setText(tr("Select hop:"));
+    selectLabel->setGeometry(QRect(10, 70, 141, 20));
+    selectLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    QLabel *instockLabel = new QLabel(dialog);
+    instockLabel->setObjectName(QString::fromUtf8("instockLabel"));
+    instockLabel->setText(tr("In stock:"));
+    instockLabel->setGeometry(QRect(525, 70, 121, 20));
+    instockLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    QLabel *ibuLabel = new QLabel(dialog);
+    ibuLabel->setObjectName(QString::fromUtf8("maxLabel"));
+    ibuLabel->setText(tr("Bitterness IBU:"));
+    ibuLabel->setGeometry(QRect(420, 130, 121, 20));
+    ibuLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+
+    hselectEdit = new QComboBox(dialog);
+    hselectEdit->setObjectName(QString::fromUtf8("selectEdit"));
+    hselectEdit->setGeometry(QRect(160, 70, 371, 23));
+
+    hnameEdit = new QLineEdit(dialog);
+    hnameEdit->setObjectName(QString::fromUtf8("hnameEdit"));
+    hnameEdit->setText(product->hops.at(product->hops_row).h_name);
+    hnameEdit->setGeometry(QRect(160, 10, 511, 23));
+    hnameEdit->setReadOnly(true);
+    horiginEdit = new QLineEdit(dialog);
+    horiginEdit->setObjectName(QString::fromUtf8("horiginEdit"));
+    horiginEdit->setText(product->hops.at(product->hops_row).h_origin);
+    horiginEdit->setGeometry(QRect(160, 40, 511, 23));
+    horiginEdit->setReadOnly(true);
+    hamountEdit = new QDoubleSpinBox(dialog);
+    hamountEdit->setObjectName(QString::fromUtf8("hamountEdit"));
+    hamountEdit->setGeometry(QRect(160, 100, 121, 24));
+    hamountEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    hamountEdit->setAccelerated(true);
+    hamountEdit->setDecimals(1);
+    hamountEdit->setMaximum(1000000.0);
+    hamountEdit->setSingleStep(0.5);
+    hamountEdit->setValue(product->hops.at(product->hops_row).h_amount * 1000.0);
+    htimeEdit = new QSpinBox(dialog);
+    htimeEdit->setObjectName(QString::fromUtf8("htimeEdit"));
+    htimeEdit->setGeometry(QRect(160, 130, 121, 24));
+    htimeEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    htimeEdit->setAccelerated(true);
+    htimeEdit->setMaximum(10000.0);
+    if (product->hops.at(product->hops_row).h_useat == 2 || product->hops.at(product->hops_row).h_useat == 4) {	// Boil or whirlpool
+	htimeEdit->setValue(product->hops.at(product->hops_row).h_time);
+	htimeEdit->setReadOnly(false);
+    } else if (product->hops.at(product->hops_row).h_useat == 5){	// Dry-hop
+	htimeEdit->setValue(product->hops.at(product->hops_row).h_time / 1440);
+	htimeEdit->setReadOnly(false);
+    } else {
+	htimeEdit->setReadOnly(true);
+    }
+    useatEdit = new QComboBox(dialog);
+    useatEdit->setObjectName(QString::fromUtf8("useatEdit"));
+    useatEdit->setGeometry(QRect(160, 160, 161, 23));
+    useatEdit->addItem(tr("Mash"));
+    useatEdit->addItem(tr("First wort"));
+    useatEdit->addItem(tr("Boil"));
+    useatEdit->addItem(tr("Aroma"));
+    useatEdit->addItem(tr("Whirlpool"));
+    useatEdit->addItem(tr("Dry hop"));
+    useatEdit->setCurrentIndex(product->hops.at(product->hops_row).h_useat);
+
+    hinstockEdit = new QCheckBox(dialog);
+    hinstockEdit->setObjectName(QString::fromUtf8("hinstockEdit"));
+    hinstockEdit->setGeometry(QRect(655, 70, 85, 21));
+    hinstockEdit->setChecked(true);
+
+    ibuEdit = new QDoubleSpinBox(dialog);
+    ibuEdit->setObjectName(QString::fromUtf8("ibuEdit"));
+    ibuEdit->setGeometry(QRect(550, 130, 121, 24));
+    ibuEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    ibuEdit->setReadOnly(true);
+    ibuEdit->setButtonSymbols(QAbstractSpinBox::NoButtons);
+    ibuEdit->setDecimals(1);
+    double ibu = Utils::toIBU(product->hops.at(product->hops_row).h_useat, product->hops.at(product->hops_row).h_form, product->preboil_sg,
+                              product->batch_size, product->hops.at(product->hops_row).h_amount, product->hops.at(product->hops_row).h_time,
+                              product->hops.at(product->hops_row).h_alpha, product->ibu_method, 0, product->hops.at(product->hops_row).h_time, 0);
+    ibuEdit->setValue(ibu);
+
+    hop_instock_changed(true);
+
+    connect(hselectEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::hop_select_changed);
+    connect(hamountEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::hop_amount_changed);
+    connect(htimeEdit, QOverload<int>::of(&QSpinBox::valueChanged), this, &EditProduct::hop_time_changed);
+    connect(useatEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::hop_useat_changed);
+    connect(hinstockEdit, &QCheckBox::stateChanged, this, &EditProduct::hop_instock_changed);
+    connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));
+    connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));
+
+    dialog->setModal(true);
+    dialog->exec();
+    if (dialog->result() == QDialog::Rejected) {
+        qDebug() << "reject and rollback";
+        product->hops[product->hops_row] = backup;
+    } else {
+	/* Clear time if hop is not used for boil, whirlpool or dry-hop. */
+	if (! (product->hops.at(product->hops_row).h_useat == 2 ||
+	       product->hops.at(product->hops_row).h_useat == 4 ||
+	       product->hops.at(product->hops_row).h_useat == 5)) {
+	    if (product->hops.at(product->hops_row).h_time) {
+		product->hops[product->hops_row].h_time = 0;
+		is_changed();
+	    }
+	}
+    }
+
+    disconnect(hselectEdit, nullptr, nullptr, nullptr);
+    disconnect(hamountEdit, nullptr, nullptr, nullptr);
+    disconnect(htimeEdit, nullptr, nullptr, nullptr);
+    disconnect(useatEdit, nullptr, nullptr, nullptr);
+    disconnect(hinstockEdit, nullptr, nullptr, nullptr);
+    disconnect(buttonBox, nullptr, nullptr, nullptr);
+
+    emit refreshAll();
+}
+
+
+void EditProduct::adjustHops(double factor)
+{
+    double amount;
+
+    if (product->hops.size() == 0)
+	return;
+
+    for (int i = 0; i < product->hops.size(); i++) {
+	amount = product->hops.at(i).h_amount * factor;
+	product->hops[i].h_amount = amount;
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EditProductTab5.cpp	Thu Apr 28 22:49:13 2022 +0200
@@ -0,0 +1,686 @@
+/**
+ * EditProduct.cpp is part of bmsapp.
+ *
+ * tab 5, miscs.
+ *
+ * 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::misc_sort_test(const Miscs &D1, const Miscs &D2)
+{
+    if (D1.m_use_use > D2.m_use_use)
+	return false;
+    if (D1.m_use_use < D2.m_use_use)
+	return true;
+    if (D1.m_type > D2.m_type)
+	return false;
+    if (D1.m_type < D2.m_type)
+	return true;
+    return (D1.m_amount > D2.m_amount);
+}
+
+
+void EditProduct::refreshMiscs()
+{
+    QString w;
+    QWidget* pWidget;
+    QHBoxLayout* pLayout;
+    QTableWidgetItem *item;
+
+    qDebug() << "refreshMiscs" << product->miscs.size();
+    std::sort(product->miscs.begin(), product->miscs.end(), misc_sort_test);
+
+    const QSignalBlocker blocker1(ui->bs_cacl2Edit);
+    const QSignalBlocker blocker2(ui->bs_caso4Edit);
+    const QSignalBlocker blocker3(ui->bs_mgso4Edit);
+    const QSignalBlocker blocker4(ui->bs_naclEdit);
+    const QSignalBlocker blocker5(ui->bs_mgcl2Edit);
+    const QSignalBlocker blocker6(ui->bs_nahco3Edit);
+    const QSignalBlocker blocker7(ui->bs_caco3Edit);
+    const QSignalBlocker blocker8(ui->mw_acidPick);
+    const QSignalBlocker blocker9(ui->mw_acidvolEdit);
+
+    const QStringList labels({tr("Ingredient"), tr("Type"), tr("Use at"), tr("Time"), tr("Amount"), tr("Delete"), tr("Edit") });
+
+    ui->miscsTable->setColumnCount(7);
+    ui->miscsTable->setColumnWidth(0, 300);	/* Ingredient	*/
+    ui->miscsTable->setColumnWidth(1, 100);	/* Type		*/
+    ui->miscsTable->setColumnWidth(2, 100);	/* Added	*/
+    ui->miscsTable->setColumnWidth(3,  75);	/* Time		*/
+    ui->miscsTable->setColumnWidth(4,  90);	/* Amount	*/
+    ui->miscsTable->setColumnWidth(5,  80);	/* Delete	*/
+    ui->miscsTable->setColumnWidth(6,  80);	/* Edit		*/
+    ui->miscsTable->setHorizontalHeaderLabels(labels);
+    ui->miscsTable->verticalHeader()->hide();
+    ui->miscsTable->setRowCount(product->miscs.size());
+
+    for (int i = 0; i < product->miscs.size(); i++) {
+
+	ui->miscsTable->setItem(i, 0, new QTableWidgetItem(product->miscs.at(i).m_name));
+
+	item = new QTableWidgetItem(misc_types[product->miscs.at(i).m_type]);
+        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+        ui->miscsTable->setItem(i, 1, item);
+
+	item = new QTableWidgetItem(misc_uses[product->miscs.at(i).m_use_use]);
+        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+        ui->miscsTable->setItem(i, 2, item);
+
+	if (product->miscs.at(i).m_use_use == 2) {	// Boil
+	    item = new QTableWidgetItem(QString("%1 min.").arg(product->miscs.at(i).m_time, 1, 'f', 0, '0'));
+	} else if (product->miscs.at(i).m_use_use == 3 || product->miscs.at(i).m_use_use == 4) {	// Primary or secondary
+	    item = new QTableWidgetItem(QString("%1 days.").arg(product->miscs.at(i).m_time / 1440, 1, 'f', 0, '0'));
+	} else {
+	    item = new QTableWidgetItem(QString(""));
+	}
+	item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->miscsTable->setItem(i, 3, item);
+
+	if (product->miscs.at(i).m_amount_is_weight)
+	    item = new QTableWidgetItem(QString("%1 gr").arg(product->miscs.at(i).m_amount * 1000.0, 3, 'f', 2, '0'));
+	else
+	    item = new QTableWidgetItem(QString("%1 ml").arg(product->miscs.at(i).m_amount * 1000.0, 3, 'f', 2, '0'));
+	item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->miscsTable->setItem(i, 4, item);
+
+	/*
+	 * Add the Delete and Edit row buttons.
+	 * Not for water agents, these are set on the water tab.
+	 */
+	if (product->miscs.at(i).m_type == 4) {
+	    ui->miscsTable->removeCellWidget(i, 5);
+	    ui->miscsTable->removeCellWidget(i, 6);
+	} 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(deleteMiscRow_clicked()));
+            pLayout = new QHBoxLayout(pWidget);
+            pLayout->addWidget(btn_dele);
+            pLayout->setContentsMargins(5, 0, 5, 0);
+            pWidget->setLayout(pLayout);
+            ui->miscsTable->setCellWidget(i, 5, 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(editMiscRow_clicked()));
+            pLayout = new QHBoxLayout(pWidget);
+            pLayout->addWidget(btn_edit);
+            pLayout->setContentsMargins(5, 0, 5, 0);
+            pWidget->setLayout(pLayout);
+            ui->miscsTable->setCellWidget(i, 6, pWidget);
+	}
+
+	/*
+	 * Update the water agents.
+	 */
+	if (product->miscs.at(i).m_type == 4) {
+	    if (product->miscs.at(i).m_name == "CaCl2") {
+		ui->bs_cacl2Edit->setValue(product->miscs.at(i).m_amount * 1000.0);
+	    } else if (product->miscs.at(i).m_name == "CaSO4") {
+		ui->bs_caso4Edit->setValue(product->miscs.at(i).m_amount * 1000.0);
+	    } else if (product->miscs.at(i).m_name == "MgSO4") {
+		ui->bs_mgso4Edit->setValue(product->miscs.at(i).m_amount * 1000.0);
+	    } else if (product->miscs.at(i).m_name == "NaCl") {
+		ui->bs_naclEdit->setValue(product->miscs.at(i).m_amount * 1000.0);
+	    } else if (product->miscs.at(i).m_name == "MgCl2") {
+		ui->bs_mgcl2Edit->setValue(product->miscs.at(i).m_amount * 1000.0);
+	    } else if (product->miscs.at(i).m_name == "NaHCO3") {
+		ui->bs_nahco3Edit->setValue(product->miscs.at(i).m_amount * 1000.0);
+	    } else if (product->miscs.at(i).m_name == "CaCO3") {
+		ui->bs_caco3Edit->setValue(product->miscs.at(i).m_amount * 1000.0);
+	    } else if (product->miscs.at(i).m_name == "Melkzuur" || product->miscs.at(i).m_name == "Lactic") {
+		product->wa_acid_name = 0;
+		product->wa_acid_perc = my_acids.at(0).AcidPrc;
+		ui->mw_acidPick->setCurrentIndex(0);
+		ui->mw_acidpercEdit->setValue(my_acids.at(0).AcidPrc);
+		ui->mw_acidvolEdit->setValue(product->miscs.at(i).m_amount * 1000.0);
+	    } else if (product->miscs.at(i).m_name == "Zoutzuur" || product->miscs.at(i).m_name == "Hydrochloric") {
+                product->wa_acid_name = 1;
+                product->wa_acid_perc = my_acids.at(1).AcidPrc;
+		ui->mw_acidPick->setCurrentIndex(1);
+                ui->mw_acidpercEdit->setValue(my_acids.at(1).AcidPrc);
+                ui->mw_acidvolEdit->setValue(product->miscs.at(i).m_amount * 1000.0);
+	    } else if (product->miscs.at(i).m_name == "Fosforzuur" || product->miscs.at(i).m_name == "Phosphoric") {
+                product->wa_acid_name = 2;
+                product->wa_acid_perc = my_acids.at(2).AcidPrc;
+		ui->mw_acidPick->setCurrentIndex(2);
+                ui->mw_acidpercEdit->setValue(my_acids.at(2).AcidPrc);
+                ui->mw_acidvolEdit->setValue(product->miscs.at(i).m_amount * 1000.0);
+	    } else if (product->miscs.at(i).m_name == "Zwavelzuur" || product->miscs.at(i).m_name == "Sulfuric") {
+                product->wa_acid_name = 3;
+                product->wa_acid_perc = my_acids.at(3).AcidPrc;
+		ui->mw_acidPick->setCurrentIndex(3);
+                ui->mw_acidpercEdit->setValue(my_acids.at(3).AcidPrc);
+                ui->mw_acidvolEdit->setValue(product->miscs.at(i).m_amount * 1000.0);
+	    }
+	}
+    }
+}
+
+
+/*
+ * Manipulate the memory array and update the miscs table.
+ */
+void EditProduct::brewing_salt_sub(QString salt, double val)
+{
+    QTableWidgetItem *item;
+
+    val = round(val * 100.0) / 100.0;
+    if (val == 0) {
+	/*
+	 * Remove this salt if it is in the table.
+	 */
+	for (int i = 0; i < product->miscs.size(); i++) {
+	    if (salt.contains(product->miscs.at(i).m_name)) {
+		qDebug() << "  brewing_salt_sub delete" << salt;
+		product->miscs.removeAt(i);
+		refreshMiscs();
+		return;
+	    }
+	}
+	qDebug() << "  brewing_salt_sub delete error";
+	return;
+    }
+
+    /*
+     * First see if this salt is in the table.
+     * If it is, update the amount.
+     */
+    for (int i = 0; i < product->miscs.size(); i++) {
+	if (salt.contains(product->miscs.at(i).m_name)) {
+	    product->miscs[i].m_amount = val / 1000.0;
+	    if (product->miscs.at(i).m_amount_is_weight)
+            	item = new QTableWidgetItem(QString("%1 gr").arg(val, 3, 'f', 2, '0'));
+            else
+	    	item = new QTableWidgetItem(QString("%1 ml").arg(val, 3, 'f', 2, '0'));
+	    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+	    ui->miscsTable->setItem(i, 4, item);
+	    return;
+	}
+    }
+
+    /*
+     * We need a new entry. Search in the database is tricky because
+     * we are here with possible more names for the same salt. The
+     * name can be like 'Lactic Melkzuur'. So we select only the
+     * brewing salts and manually check their names.
+     */
+    QSqlQuery query("SELECT * FROM inventory_miscs WHERE type = 4");
+    while (query.next()) {
+	if (salt.contains(query.value(1).toString())) {
+	    qDebug() << "  found it, append";
+	    Miscs m;
+	    m.m_name = query.value(1).toString();
+            m.m_amount = val / 1000.0;
+            m.m_type = query.value(2).toInt();
+            m.m_use_use = query.value(3).toInt();
+            m.m_time = query.value(4).toDouble();
+            m.m_amount_is_weight = query.value(5).toInt() ? true:false;
+            m.m_cost = query.value(10).toDouble();
+            product->miscs.append(m);
+	    refreshMiscs();
+	    return;
+	}
+    }
+
+    qDebug() << "brewing_salt_sub, nothing done." << salt << val;
+}
+
+
+/*
+ * Edit brewing salt and recalculate.
+ */
+void EditProduct::set_brewing_salt(QString salt, double val)
+{
+    val = round(val * 100.0) / 100.0;
+    qDebug() << "set_brewing_salt" << salt << val;
+    brewing_salt_sub(salt, val);
+    calcWater();
+    is_changed();
+}
+
+
+void EditProduct::addMiscRow_clicked()
+{
+    Miscs newm;
+
+    for (int i = 0; i < product->miscs.size(); i++) {
+        if (product->miscs.at(i).m_amount == 0)
+            return;     // Add only one at a time.
+    }
+
+    newm.m_name = "Select one";
+    newm.m_amount = 0;
+    newm.m_type = 0;
+    newm.m_use_use = 0;
+    newm.m_time = 0;
+    newm.m_amount_is_weight = true;
+    newm.m_cost = 0;
+    product->miscs.append(newm);
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::deleteMiscRow_clicked()
+{
+    if (product->locked || product->miscs.size() < 1)
+	return;
+
+    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
+    int row = pb->objectName().toInt();
+    qDebug() << "Delete misc row" << row << product->miscs.size();
+
+    int rc = QMessageBox::warning(this, tr("Delete misc"), tr("Delete %1").arg(product->miscs.at(row).m_name),
+                    QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
+    if (rc == QMessageBox::No)
+        return;
+
+    product->miscs.removeAt(row);
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::misc_amount_changed(double val)
+{
+    QTableWidgetItem *item;
+
+    qDebug() << "misc_amount_changed()" << product->miscs_row << val;
+
+    product->miscs[product->miscs_row].m_amount = val / 1000.0;
+    if (product->miscs.at(product->miscs_row).m_amount_is_weight) {
+        item = new QTableWidgetItem(QString("%1 gr").arg(product->miscs.at(product->miscs_row).m_amount * 1000.0, 3, 'f', 2, '0'));
+    } else {
+        item = new QTableWidgetItem(QString("%1 ml").arg(product->miscs.at(product->miscs_row).m_amount * 1000.0, 3, 'f', 2, '0'));
+    }
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->miscsTable->setItem(product->miscs_row, 4, item);
+
+    is_changed();
+}
+
+
+void EditProduct::misc_time_changed(int val)
+{
+    QTableWidgetItem *item;
+
+    qDebug() << "misc_time_changed()" << product->miscs_row << val;
+
+    if (product->miscs.at(product->miscs_row).m_use_use == 2) {       // Boil
+	product->miscs[product->miscs_row].m_time = val;
+	item = new QTableWidgetItem(QString("%1 min.").arg(val, 1, 'f', 0, '0'));
+    } else if (product->miscs.at(product->miscs_row).m_use_use == 3 || product->miscs.at(product->miscs_row).m_use_use == 4) {  // Primary or secondary
+	product->miscs[product->miscs_row].m_time = val * 1440;
+	item = new QTableWidgetItem(QString("%1 days.").arg(val, 1, 'f', 0, '0'));
+    } else {
+	item = new QTableWidgetItem(QString(""));
+    }
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->miscsTable->setItem(product->miscs_row, 3, item);
+
+    is_changed();
+}
+
+
+void EditProduct::misc_select_changed(int val)
+{
+    QSqlQuery query;
+    bool instock = minstockEdit->isChecked();
+    QString w;
+    QTableWidgetItem *item;
+
+    if (val < 1)
+        return;
+
+    qDebug() << "misc_select_changed()" << product->miscs_row << val << instock;
+
+    /*
+     * Search the misc ingredient pointed by the index and instock flag.
+     */
+    QString sql = "SELECT name,type,use_use,time,amount_is_weight,cost FROM inventory_miscs WHERE ";
+    if (instock)
+        sql.append("inventory > 0 AND ");
+    sql.append("type != 4 ORDER BY name");
+    qDebug() << sql;
+    query.prepare(sql);
+    query.exec();
+    query.first();
+    for (int i = 0; i < (val - 1); i++) {
+        query.next();
+    }
+    qDebug() << "found" << query.value(0).toString();
+
+    /*
+     * Replace the misc record contents
+     */
+    product->miscs[product->miscs_row].m_name = query.value(0).toString();
+    product->miscs[product->miscs_row].m_type = query.value(1).toInt();
+    product->miscs[product->miscs_row].m_use_use = query.value(2).toInt();
+    product->miscs[product->miscs_row].m_time = query.value(3).toDouble();
+    product->miscs[product->miscs_row].m_amount_is_weight = query.value(4).toInt() ? true:false;
+    product->miscs[product->miscs_row].m_cost = query.value(5).toDouble();
+
+    /*
+     * Update the visible fields
+     */
+    mnameEdit->setText(product->miscs.at(product->miscs_row).m_name);
+    ui->miscsTable->setItem(product->miscs_row, 0, new QTableWidgetItem(product->miscs.at(product->miscs_row).m_name));
+
+    item = new QTableWidgetItem(misc_types[product->miscs.at(product->miscs_row).m_type]);
+    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+    ui->miscsTable->setItem(product->miscs_row, 1, item);
+
+    item = new QTableWidgetItem(misc_uses[product->miscs.at(product->miscs_row).m_use_use]);
+    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+    ui->miscsTable->setItem(product->miscs_row, 2, item);
+    useatEdit->setCurrentIndex(product->miscs.at(product->miscs_row).m_use_use);
+
+    if (product->miscs.at(product->miscs_row).m_use_use == 3 || product->miscs.at(product->miscs_row).m_use_use == 4) {     // Fermentation stages
+        mtimeEdit->setValue(product->miscs.at(product->miscs_row).m_time / 1440);
+        mtimeEdit->setReadOnly(false);
+	mtimeLabel->setText(tr("Time in days:"));
+	item = new QTableWidgetItem(QString("%1 days.").arg(product->miscs.at(product->miscs_row).m_time / 1440, 1, 'f', 0, '0'));
+    } else if (product->miscs.at(product->miscs_row).m_use_use == 2) {    // Boil
+        mtimeEdit->setValue(product->miscs.at(product->miscs_row).m_time);
+        mtimeEdit->setReadOnly(false);
+	mtimeLabel->setText(tr("Time in minutes:"));
+	item = new QTableWidgetItem(QString("%1 min.").arg(product->miscs.at(product->miscs_row).m_time, 1, 'f', 0, '0'));
+    } else {
+        mtimeEdit->setReadOnly(true);
+	mtimeLabel->setText("");
+	item = new QTableWidgetItem(QString(""));
+    }
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->miscsTable->setItem(product->miscs_row, 3, item);
+
+    if (product->miscs.at(product->miscs_row).m_amount_is_weight) {
+	mamountLabel->setText(tr("Amount in gr:"));
+	item = new QTableWidgetItem(QString("%1 gr").arg(product->miscs.at(product->miscs_row).m_amount * 1000.0, 3, 'f', 2, '0'));
+    } else {
+	mamountLabel->setText(tr("Amount in ml:"));
+	item = new QTableWidgetItem(QString("%1 ml").arg(product->miscs.at(product->miscs_row).m_amount * 1000.0, 3, 'f', 2, '0'));
+    }
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->miscsTable->setItem(product->miscs_row, 4, item);
+
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::misc_instock_changed(bool val)
+{
+    QSqlQuery query;
+
+    qDebug() << "misc_instock_changed()" << product->miscs_row << val;
+
+    this->mselectEdit->setCurrentIndex(-1);
+    this->mselectEdit->clear();
+    QString sql = "SELECT name,type,amount_is_weight,inventory FROM inventory_miscs WHERE ";
+    if (val)
+        sql.append("inventory > 0 AND ");
+    sql.append("type != 4 ORDER BY name");
+    query.prepare(sql);
+    query.exec();
+    query.first();
+    this->mselectEdit->addItem("");      // Start with empty value
+    for (int i = 0; i < query.size(); i++) {
+        this->mselectEdit->addItem(query.value(0).toString()+ " (" + misc_types[query.value(1).toInt()] + ") " +
+                        QString("%1 %2").arg(query.value(3).toDouble() * 1000.0, 3, 'f', 2, '0').arg(query.value(2).toInt()?"gr":"ml"));
+        query.next();
+    }
+}
+
+
+void EditProduct::misc_useat_changed(int val)
+{
+    QTableWidgetItem *item;
+
+    qDebug() << "misc_useat_changed" << val;
+    product->miscs[product->miscs_row].m_use_use = val;
+    item = new QTableWidgetItem(misc_uses[val]);
+    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+    ui->miscsTable->setItem(product->miscs_row, 2, item);
+
+    if (val == 3 || val == 4) {     // Fermentation stages
+	product->miscs[product->miscs_row].m_time = mtimeEdit->value() * 1440;
+        mtimeEdit->setReadOnly(false);
+        mtimeLabel->setText(tr("Time in days:"));
+        item = new QTableWidgetItem(QString("%1 days.").arg(product->miscs.at(product->miscs_row).m_time / 1440, 1, 'f', 0, '0'));
+    } else if (val == 2) {    // Boil
+	product->miscs[product->miscs_row].m_time = mtimeEdit->value();
+        mtimeEdit->setReadOnly(false);
+        mtimeLabel->setText(tr("Time in minutes:"));
+        item = new QTableWidgetItem(QString("%1 min.").arg(product->miscs.at(product->miscs_row).m_time, 1, 'f', 0, '0'));
+    } else {
+	product->miscs[product->miscs_row].m_time = 0;
+	mtimeEdit->setValue(0);
+        mtimeEdit->setReadOnly(true);
+        mtimeLabel->setText("");
+        item = new QTableWidgetItem(QString(""));
+    }
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->miscsTable->setItem(product->miscs_row, 3, item);
+
+    is_changed();
+}
+
+
+void EditProduct::editMiscRow_clicked()
+{
+    QSqlQuery query;
+
+    if (product->locked)
+	return;
+
+    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
+    product->miscs_row = pb->objectName().toInt();
+    qDebug() << "Edit misc row" << product->miscs_row;
+    Miscs backup = product->miscs.at(product->miscs_row);
+
+    QDialog* dialog = new QDialog(this);
+    dialog->resize(738, 230);
+    QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
+    buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
+    buttonBox->setGeometry(QRect(30, 180, 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);
+
+    mamountLabel = new QLabel(dialog);
+    mamountLabel->setObjectName(QString::fromUtf8("mamountLabel"));
+    if (product->miscs.at(product->miscs_row).m_amount_is_weight)
+    	mamountLabel->setText(tr("Amount in gr:"));
+    else
+	mamountLabel->setText(tr("Amount in ml:"));
+    mamountLabel->setGeometry(QRect(10, 70, 141, 20));
+    mamountLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+
+    mtimeLabel = new QLabel(dialog);
+    mtimeLabel->setObjectName(QString::fromUtf8("mtimeLabel"));
+    if (product->miscs.at(product->miscs_row).m_use_use == 3 || product->miscs.at(product->miscs_row).m_use_use == 4)	// Fermentation stages
+        mtimeLabel->setText(tr("Time in days:"));
+    else if (product->miscs.at(product->miscs_row).m_use_use == 2)  // Boil
+        mtimeLabel->setText(tr("Time in minutes:"));
+    else
+        mtimeLabel->setText("");
+    mtimeLabel->setGeometry(QRect(10, 100, 141, 20));
+    mtimeLabel->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, 40, 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, 40, 121, 20));
+    instockLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+
+    mselectEdit = new QComboBox(dialog);
+    mselectEdit->setObjectName(QString::fromUtf8("selectEdit"));
+    mselectEdit->setGeometry(QRect(160, 40, 371, 23));
+
+    mnameEdit = new QLineEdit(dialog);
+    mnameEdit->setObjectName(QString::fromUtf8("mnameEdit"));
+    mnameEdit->setText(product->miscs.at(product->miscs_row).m_name);
+    mnameEdit->setGeometry(QRect(160, 10, 511, 23));
+    mnameEdit->setReadOnly(true);
+
+    mamountEdit = new QDoubleSpinBox(dialog);
+    mamountEdit->setObjectName(QString::fromUtf8("mamountEdit"));
+    mamountEdit->setGeometry(QRect(160, 70, 121, 24));
+    mamountEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    mamountEdit->setAccelerated(true);
+    mamountEdit->setDecimals(2);
+    mamountEdit->setMaximum(1000000.0);
+    mamountEdit->setSingleStep(0.1);
+    mamountEdit->setValue(product->miscs.at(product->miscs_row).m_amount * 1000.0);
+
+    mtimeEdit = new QSpinBox(dialog);
+    mtimeEdit->setObjectName(QString::fromUtf8("mtimeEdit"));
+    mtimeEdit->setGeometry(QRect(160, 100, 121, 24));
+    mtimeEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    mtimeEdit->setAccelerated(true);
+    mtimeEdit->setMaximum(10000.0);
+    if (product->miscs.at(product->miscs_row).m_use_use == 3 || product->miscs.at(product->miscs_row).m_use_use == 4) {	// Fermentation stages
+	mtimeEdit->setValue(product->miscs.at(product->miscs_row).m_time / 1440);
+        mtimeEdit->setReadOnly(false);
+    } else if (product->miscs.at(product->miscs_row).m_use_use == 2) {	// Boil
+	mtimeEdit->setValue(product->miscs.at(product->miscs_row).m_time);
+        mtimeEdit->setReadOnly(false);
+    } else {
+	mtimeEdit->setReadOnly(true);
+    }
+
+    useatEdit = new QComboBox(dialog);
+    useatEdit->setObjectName(QString::fromUtf8("useatEdit"));
+    useatEdit->setGeometry(QRect(160, 130, 161, 23));
+    useatEdit->addItem(tr("Starter"));
+    useatEdit->addItem(tr("Mash"));
+    useatEdit->addItem(tr("Boil"));
+    useatEdit->addItem(tr("Primary"));
+    useatEdit->addItem(tr("Secondary"));
+    useatEdit->addItem(tr("Bottling"));
+    useatEdit->setCurrentIndex(product->miscs.at(product->miscs_row).m_use_use);
+
+    minstockEdit = new QCheckBox(dialog);
+    minstockEdit->setObjectName(QString::fromUtf8("minstockEdit"));
+    minstockEdit->setGeometry(QRect(655, 40, 85, 21));
+    minstockEdit->setChecked(true);
+
+    misc_instock_changed(true);
+
+    connect(mselectEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::misc_select_changed);
+    connect(mamountEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::misc_amount_changed);
+    connect(mtimeEdit, QOverload<int>::of(&QSpinBox::valueChanged), this, &EditProduct::misc_time_changed);
+    connect(useatEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::misc_useat_changed);
+    connect(minstockEdit, &QCheckBox::stateChanged, this, &EditProduct::misc_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->miscs[product->miscs_row] = backup;
+    } else {
+        /* Clear time if misc is not used for boil or fermentation. */
+        if (! (product->miscs.at(product->miscs_row).m_use_use == 2 ||
+               product->miscs.at(product->miscs_row).m_use_use == 3 ||
+               product->miscs.at(product->miscs_row).m_use_use == 4)) {
+            if (product->miscs.at(product->miscs_row).m_time) {
+                product->miscs[product->miscs_row].m_time = 0;
+                is_changed();
+            }
+        }
+    }
+
+    disconnect(mselectEdit, nullptr, nullptr, nullptr);
+    disconnect(mamountEdit, nullptr, nullptr, nullptr);
+    disconnect(mtimeEdit, nullptr, nullptr, nullptr);
+    disconnect(useatEdit, nullptr, nullptr, nullptr);
+    disconnect(minstockEdit, nullptr, nullptr, nullptr);
+    disconnect(buttonBox, nullptr, nullptr, nullptr);
+
+    emit refreshAll();
+}
+
+
+void EditProduct::adjustMiscs(double factor)
+{
+    double amount;
+
+    if (product->miscs.size() == 0)
+	return;
+
+    const QSignalBlocker blocker1(ui->bs_cacl2Edit);
+    const QSignalBlocker blocker2(ui->bs_caso4Edit);
+    const QSignalBlocker blocker3(ui->bs_mgso4Edit);
+    const QSignalBlocker blocker4(ui->bs_naclEdit);
+    const QSignalBlocker blocker5(ui->bs_mgcl2Edit);
+    const QSignalBlocker blocker6(ui->bs_nahco3Edit);
+    const QSignalBlocker blocker7(ui->bs_caco3Edit);
+    const QSignalBlocker blocker9(ui->mw_acidvolEdit);
+
+    for (int i = 0; i < product->miscs.size(); i++) {
+	amount = product->miscs.at(i).m_amount * factor;
+	product->miscs[i].m_amount = amount;
+
+	/*
+         * Update the water agents.
+         */
+        if (product->miscs.at(i).m_type == 4) {
+            if (product->miscs.at(i).m_name == "CaCl2") {
+                ui->bs_cacl2Edit->setValue(product->miscs.at(i).m_amount * 1000.0);
+            } else if (product->miscs.at(i).m_name == "CaSO4") {
+                ui->bs_caso4Edit->setValue(product->miscs.at(i).m_amount * 1000.0);
+            } else if (product->miscs.at(i).m_name == "MgSO4") {
+                ui->bs_mgso4Edit->setValue(product->miscs.at(i).m_amount * 1000.0);
+            } else if (product->miscs.at(i).m_name == "NaCl") {
+                ui->bs_naclEdit->setValue(product->miscs.at(i).m_amount * 1000.0);
+            } else if (product->miscs.at(i).m_name == "MgCl2") {
+                ui->bs_mgcl2Edit->setValue(product->miscs.at(i).m_amount * 1000.0);
+            } else if (product->miscs.at(i).m_name == "NaHCO3") {
+                ui->bs_nahco3Edit->setValue(product->miscs.at(i).m_amount * 1000.0);
+            } else if (product->miscs.at(i).m_name == "CaCO3") {
+                ui->bs_caco3Edit->setValue(product->miscs.at(i).m_amount * 1000.0);
+            } else if (product->miscs.at(i).m_name == "Melkzuur" || product->miscs.at(i).m_name == "Lactic") {
+                ui->mw_acidvolEdit->setValue(product->miscs.at(i).m_amount * 1000.0);
+            } else if (product->miscs.at(i).m_name == "Zoutzuur" || product->miscs.at(i).m_name == "Hydrochloric") {
+                ui->mw_acidvolEdit->setValue(product->miscs.at(i).m_amount * 1000.0);
+            } else if (product->miscs.at(i).m_name == "Fosforzuur" || product->miscs.at(i).m_name == "Phosphoric") {
+                ui->mw_acidvolEdit->setValue(product->miscs.at(i).m_amount * 1000.0);
+            } else if (product->miscs.at(i).m_name == "Zwavelzuur" || product->miscs.at(i).m_name == "Sulfuric") {
+                ui->mw_acidvolEdit->setValue(product->miscs.at(i).m_amount * 1000.0);
+            }
+        }
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EditProductTab6.cpp	Thu Apr 28 22:49:13 2022 +0200
@@ -0,0 +1,593 @@
+/**
+ * EditProduct.cpp is part of bmsapp.
+ *
+ * tab 6, yeasts.
+ *
+ * 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 yeaste 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::yeast_sort_test(const Yeasts &D1, const Yeasts &D2)
+{
+    if (D1.y_use > D2.y_use)
+	return false;
+    if (D1.y_use < D2.y_use)
+	return true;
+    return (D1.y_amount > D2.y_amount);
+}
+
+
+void EditProduct::refreshYeasts()
+{
+    QString w;
+    QWidget* pWidget;
+    QHBoxLayout* pLayout;
+    QTableWidgetItem *item;
+
+    qDebug() << "refreshYeasts" << product->yeasts.size();
+    std::sort(product->yeasts.begin(), product->yeasts.end(), yeast_sort_test);
+
+    const QStringList labels({tr("Yeast"), tr("Laboratory"), tr("Code"), tr("Type"), tr("Use for"), tr("Min. °C"), tr("Max. °C"),
+		              tr("Tol. %"), tr("Attn. %"), tr("Amount"), tr("Delete"), tr("Edit") });
+
+    ui->yeastsTable->setColumnCount(12);
+    ui->yeastsTable->setColumnWidth(0, 200);	/* Yeast	*/
+    ui->yeastsTable->setColumnWidth(1, 125);	/* Laboratory	*/
+    ui->yeastsTable->setColumnWidth(2,  80);	/* Code		*/
+    ui->yeastsTable->setColumnWidth(3,  80);	/* Type		*/
+    ui->yeastsTable->setColumnWidth(4, 100);	/* Usage	*/
+    ui->yeastsTable->setColumnWidth(5,  60);	/* Min. 	*/
+    ui->yeastsTable->setColumnWidth(6,  60);	/* Max.		*/
+    ui->yeastsTable->setColumnWidth(7,  60);	/* Tolerance	*/
+    ui->yeastsTable->setColumnWidth(8,  60);	/* Attenuation	*/
+    ui->yeastsTable->setColumnWidth(9,  90);	/* Amount	*/
+    ui->yeastsTable->setColumnWidth(10, 80);	/* Delete	*/
+    ui->yeastsTable->setColumnWidth(11, 80);	/* Edit		*/
+    ui->yeastsTable->setHorizontalHeaderLabels(labels);
+    ui->yeastsTable->verticalHeader()->hide();
+    ui->yeastsTable->setRowCount(product->yeasts.size());
+
+    for (int i = 0; i < product->yeasts.size(); i++) {
+
+	ui->yeastsTable->setItem(i, 0, new QTableWidgetItem(product->yeasts.at(i).y_name));
+	ui->yeastsTable->setItem(i, 1, new QTableWidgetItem(product->yeasts.at(i).y_laboratory));
+	ui->yeastsTable->setItem(i, 2, new QTableWidgetItem(product->yeasts.at(i).y_product_id));
+
+	item = new QTableWidgetItem(yeast_forms[product->yeasts.at(i).y_form]);
+        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+        ui->yeastsTable->setItem(i, 3, item);
+
+        item = new QTableWidgetItem(yeast_use[product->yeasts.at(i).y_use]);
+        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+        ui->yeastsTable->setItem(i, 4, item);
+
+	item = new QTableWidgetItem(QString("%1").arg(product->yeasts.at(i).y_min_temperature, 2, 'f', 1, '0'));
+	item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->yeastsTable->setItem(i, 5, item);
+
+	item = new QTableWidgetItem(QString("%1").arg(product->yeasts.at(i).y_max_temperature, 2, 'f', 1, '0'));
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->yeastsTable->setItem(i, 6, item);
+
+	item = new QTableWidgetItem(QString("%1").arg(product->yeasts.at(i).y_tolerance, 2, 'f', 1, '0'));
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->yeastsTable->setItem(i, 7, item);
+
+	item = new QTableWidgetItem(QString("%1").arg(product->yeasts.at(i).y_attenuation, 2, 'f', 1, '0'));
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->yeastsTable->setItem(i, 8, item);
+
+	if (product->yeasts.at(i).y_form == 0)
+            item = new QTableWidgetItem(QString("%1 pack").arg(product->yeasts.at(i).y_amount, 1, 'f', 0, '0'));
+	else if (product->yeasts.at(i).y_form == 1)
+	    item = new QTableWidgetItem(QString("%1 gr").arg(product->yeasts.at(i).y_amount * 1000.0, 3, 'f', 2, '0'));
+	else
+	    item = new QTableWidgetItem(QString("%1 ml").arg(product->yeasts.at(i).y_amount * 1000.0, 3, 'f', 2, '0'));
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->yeastsTable->setItem(i, 9, item);
+
+	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(deleteYeastRow_clicked()));
+        pLayout = new QHBoxLayout(pWidget);
+        pLayout->addWidget(btn_dele);
+        pLayout->setContentsMargins(5, 0, 5, 0);
+        pWidget->setLayout(pLayout);
+        ui->yeastsTable->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(editYeastRow_clicked()));
+        pLayout = new QHBoxLayout(pWidget);
+        pLayout->addWidget(btn_edit);
+        pLayout->setContentsMargins(5, 0, 5, 0);
+        pWidget->setLayout(pLayout);
+        ui->yeastsTable->setCellWidget(i, 11, pWidget);
+    }
+}
+
+
+/*
+ * The results are not stored line in EditProduct. This is just a hint.
+ */
+void EditProduct::calcYeast()
+{
+    double sg = product->est_og;
+    double plato = Utils::sg_to_plato(sg);
+    double volume = product->batch_size * 0.9;	// Volume min trub chiller loss.
+    bool maybe_starter = false;
+    double pitchrate = 0.75;
+    double initcells = 0;
+
+    qDebug() << "calcYeast()";
+    ui->yeastProcedure->setCurrentIndex(0);
+
+    if (product->yeasts.size() == 0)
+	return;		// No yeast in product.
+
+    for (int i = 0; i < product->yeasts.size(); i++) {
+	if (product->yeasts.at(i).y_use == 0) {		// Primary
+	    if (product->yeasts.at(i).y_form == 1) {
+		/*
+		 * Dry yeast, build the formule with the yeast parameters.
+		 * Based on https://www.lallemandbrewing.com/en/canada/brewers-corner/brewing-tools/pitching-rate-calculator/
+		 */
+		ui->yeastProcedure->setCurrentIndex(2);
+		ui->lo_gr_hlEdit->setValue(product->yeasts.at(i).y_gr_hl_lo);
+		ui->hi_gr_hlEdit->setValue(product->yeasts.at(i).y_gr_hl_hi);
+		ui->lo_sgEdit->setValue(product->yeasts.at(i).y_sg_lo);
+		ui->hi_sgEdit->setValue(product->yeasts.at(i).y_sg_hi);
+		double og = product->yeasts.at(i).y_sg_lo;
+		double f1 = product->yeasts.at(i).y_gr_hl_lo / 100.0;
+		double f2 = round(f1 / 5 * 1000000.0) / 1000000.0;
+     		double multiplier = (sg <= og) ? f1 : (f1 + f2 * (sg - og) / 0.008);
+		qDebug() << "  sg:" << sg << "og:" << og << "f1:" << f1 << "f2:" << f2 << "multiplier:" << multiplier;
+     		double yeast_grams = round(volume * multiplier * 100.0) / 100.0; // * (100 / dataRecord.starter_viability), 2);
+     		double yeast_gr_hl = round((yeast_grams / (volume * 0.01)) * 100.0) / 100.0;
+		ui->need_grEdit->setValue(yeast_grams);
+		ui->pitch_grEdit->setValue(yeast_gr_hl);
+     		qDebug() << "  need" << yeast_grams << "grams, gr/hl:" << yeast_gr_hl;
+		return;
+	    } else {
+		/*
+		 * Liquid, slant, culture etc.
+		 * pitchrate see https://www.brewersfriend.com/yeast-pitch-rate-and-starter-calculator/
+		 * and http://braukaiser.com/blog/blog/2012/11/03/estimating-yeast-growth/
+		 */
+		ui->yeastProcedure->setCurrentIndex(1);
+		if (product->yeasts.at(i).y_type == 0) {		// Lager yeast
+		    pitchrate = 1.5;
+		    if (sg > 1.060)
+			pitchrate = 2.0;
+		} else if (product->yeasts.at(i).y_type == 6) {	// Real Kveik
+		    pitchrate = 0.075;
+		} else {
+		    pitchrate = 0.75;
+		    if (sg > 1.060)
+			pitchrate = 1.0;
+		}
+		if (product->yeasts.at(i).y_form == 0)
+		    initcells = (product->yeasts.at(i).y_cells / 1000000000) * product->yeasts.at(i).y_amount * 0.97;	// 97% viability assumed.
+		else
+		    initcells = (product->yeasts.at(i).y_cells / 1000000) * product->yeasts.at(i).y_amount * 0.97;
+
+		double needed = round(pitchrate * volume * plato * 10.0) / 10.0;
+		double starter = 0;
+		if (needed > initcells) {
+		    maybe_starter = true;
+		    starter = round(needed / 2.0) / 100.0;	// A very rough starter size estimate.
+		}
+
+		ui->pitchrateEdit->setValue(pitchrate);
+		ui->initcellsEdit->setValue(initcells);
+		ui->targetcellsEdit->setValue(needed);
+		ui->starterEdit->setValue(starter);
+
+		qDebug() << "  pitchrate:" << pitchrate << "needed:" << needed << "initcells:" << initcells << "starter" << maybe_starter << "size" << starter;
+	    }
+	    break;
+	}
+    }
+}
+
+
+void EditProduct::addYeastRow_clicked()
+{
+    Yeasts newy;
+
+    qDebug() << "Add yeast row";
+
+    for (int i = 0; i < product->yeasts.size(); i++) {
+        if (product->yeasts.at(i).y_amount == 0)
+            return;     // Add only one at a time.
+    }
+
+    newy.y_name = "Select one";
+    newy.y_laboratory = "";
+    newy.y_product_id = "";
+    newy.y_amount = 0;
+    newy.y_type = 0;
+    newy.y_form = 0;
+    newy.y_min_temperature = 0;
+    newy.y_max_temperature = 0;
+    newy.y_flocculation = 0;
+    newy.y_attenuation = 0;
+    newy.y_cells = 0;
+    newy.y_tolerance = 0;
+    newy.y_inventory = 0;
+    newy.y_use = 0;
+    newy.y_sta1 = false;
+    newy.y_bacteria = false;
+    newy.y_harvest_top = false;
+    newy.y_harvest_time = 0;
+    newy.y_pitch_temperature = 0;
+    newy.y_pofpos = false;
+    newy.y_zymocide = 0;
+    newy.y_gr_hl_lo = 0;
+    newy.y_sg_lo = 0;
+    newy.y_gr_hl_hi = 0;
+    newy.y_sg_hi = 0;
+    newy.y_cost = 0;
+
+    product->yeasts.append(newy);
+    emit refreshAll();
+}
+
+
+void EditProduct::deleteYeastRow_clicked()
+{
+    if (product->locked || product->yeasts.size() < 1)
+	return;
+
+    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
+    int row = pb->objectName().toInt();
+    qDebug() << "Delete yeast row" << row << product->yeasts.size();
+
+    int rc = QMessageBox::warning(this, tr("Delete yeast"), tr("Delete %1").arg(product->yeasts.at(row).y_name),
+                    QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
+    if (rc == QMessageBox::No)
+        return;
+
+    product->yeasts.removeAt(row);
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::yeast_amount_changed(double val)
+{
+    QTableWidgetItem *item;
+
+    qDebug() << "yeast_amount_changed()" << product->yeasts_row << val;
+
+    if (product->yeasts.at(product->yeasts_row).y_form == 0) {
+	product->yeasts[product->yeasts_row].y_amount = val;
+        item = new QTableWidgetItem(QString("%1 pack").arg(val, 1, 'f', 0, '0'));
+    } else if (product->yeasts.at(product->yeasts_row).y_form == 1) {
+	product->yeasts[product->yeasts_row].y_amount = val / 1000.0;
+        item = new QTableWidgetItem(QString("%1 gr").arg(val, 3, 'f', 2, '0'));
+    } else {
+        product->yeasts[product->yeasts_row].y_amount = val / 1000.0;
+        item = new QTableWidgetItem(QString("%1 ml").arg(val, 3, 'f', 2, '0'));
+    }
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->yeastsTable->setItem(product->yeasts_row, 9, item);
+
+    calcYeast();
+    is_changed();
+}
+
+
+void EditProduct::yeast_select_changed(int val)
+{
+    QSqlQuery query;
+    bool instock = yinstockEdit->isChecked();
+    QString w;
+    QTableWidgetItem *item;
+
+    if (val < 1)
+        return;
+
+    qDebug() << "yeast_select_changed()" << product->yeasts_row << val << instock;
+
+    /*
+     * Search the yeast pointed by the index and instock flag.
+     */
+    QString sql = "SELECT name,laboratory,product_id,type,form,min_temperature,max_temperature,flocculation,attenuation,"
+	          "cells,tolerance,sta1,bacteria,harvest_top,harvest_time,pitch_temperature,pofpos,zymocide,"
+		  "gr_hl_lo,sg_lo,gr_hl_hi,sg_hi,cost FROM inventory_yeasts ";
+    if (instock)
+        sql.append("WHERE inventory > 0 ");
+    sql.append("ORDER BY laboratory,product_id,name");
+    query.prepare(sql);
+    query.exec();
+    query.first();
+    for (int i = 0; i < (val - 1); i++) {
+        query.next();
+    }
+    qDebug() << "found" << query.value(0).toString() << query.value(2).toString();
+
+    /*
+     * Replace the yeast record contents
+     */
+    product->yeasts[product->yeasts_row].y_name = query.value(0).toString();
+    product->yeasts[product->yeasts_row].y_laboratory = query.value(1).toString();
+    product->yeasts[product->yeasts_row].y_product_id = query.value(2).toString();
+    product->yeasts[product->yeasts_row].y_type = query.value(3).toInt();
+    product->yeasts[product->yeasts_row].y_form = query.value(4).toInt();
+    product->yeasts[product->yeasts_row].y_min_temperature = query.value(5).toDouble();
+    product->yeasts[product->yeasts_row].y_max_temperature = query.value(6).toDouble();
+    product->yeasts[product->yeasts_row].y_flocculation = query.value(7).toInt();
+    product->yeasts[product->yeasts_row].y_attenuation = query.value(8).toDouble();
+    product->yeasts[product->yeasts_row].y_cells = query.value(9).toDouble();
+    product->yeasts[product->yeasts_row].y_tolerance = query.value(10).toDouble();
+    product->yeasts[product->yeasts_row].y_sta1 = query.value(11).toInt() ? true:false;
+    product->yeasts[product->yeasts_row].y_bacteria = query.value(12).toInt() ? true:false;
+    product->yeasts[product->yeasts_row].y_harvest_top = query.value(13).toInt() ? true:false;
+    product->yeasts[product->yeasts_row].y_harvest_time = query.value(14).toInt();
+    product->yeasts[product->yeasts_row].y_pitch_temperature = query.value(15).toDouble();
+    product->yeasts[product->yeasts_row].y_pofpos = query.value(16).toInt() ? true:false;
+    product->yeasts[product->yeasts_row].y_zymocide = query.value(17).toInt();
+    product->yeasts[product->yeasts_row].y_gr_hl_lo = query.value(18).toInt();
+    product->yeasts[product->yeasts_row].y_sg_lo = query.value(19).toDouble();
+    product->yeasts[product->yeasts_row].y_gr_hl_hi = query.value(20).toInt();
+    product->yeasts[product->yeasts_row].y_sg_hi = query.value(21).toDouble();
+    product->yeasts[product->yeasts_row].y_cost = query.value(22).toDouble();
+
+    /*
+     * Update the visible fields
+     */
+    ynameEdit->setText(product->yeasts.at(product->yeasts_row).y_name);
+    ylaboratoryEdit->setText(product->yeasts.at(product->yeasts_row).y_laboratory);
+    yproduct_idEdit->setText(product->yeasts.at(product->yeasts_row).y_product_id);
+    if (product->yeasts.at(product->yeasts_row).y_form == 0) {
+	yamountEdit->setDecimals(0);
+	yamountEdit->setSingleStep(1.0);
+	yamountLabel->setText(tr("Total packs:"));
+    } else if (product->yeasts.at(product->yeasts_row).y_form == 1) {
+	yamountEdit->setDecimals(1);
+    	yamountEdit->setSingleStep(0.5);
+	yamountLabel->setText(tr("Amount in gr:"));
+    } else {
+	yamountEdit->setDecimals(1);
+	yamountEdit->setSingleStep(0.5);
+	yamountLabel->setText(tr("Amount in ml:"));
+    }
+
+    ui->yeastsTable->setItem(product->yeasts_row, 0, new QTableWidgetItem(product->yeasts.at(product->yeasts_row).y_name));
+    ui->yeastsTable->setItem(product->yeasts_row, 1, new QTableWidgetItem(product->yeasts.at(product->yeasts_row).y_laboratory));
+    ui->yeastsTable->setItem(product->yeasts_row, 2, new QTableWidgetItem(product->yeasts.at(product->yeasts_row).y_product_id));
+
+    item = new QTableWidgetItem(yeast_forms[product->yeasts.at(product->yeasts_row).y_form]);
+    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+    ui->yeastsTable->setItem(product->yeasts_row, 3, item);
+
+    item = new QTableWidgetItem(QString("%1").arg(product->yeasts.at(product->yeasts_row).y_min_temperature, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->yeastsTable->setItem(product->yeasts_row, 5, item);
+
+    item = new QTableWidgetItem(QString("%1").arg(product->yeasts.at(product->yeasts_row).y_max_temperature, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->yeastsTable->setItem(product->yeasts_row, 6, item);
+
+    item = new QTableWidgetItem(QString("%1").arg(product->yeasts.at(product->yeasts_row).y_tolerance, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->yeastsTable->setItem(product->yeasts_row, 7, item);
+
+    item = new QTableWidgetItem(QString("%1").arg(product->yeasts.at(product->yeasts_row).y_attenuation, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->yeastsTable->setItem(product->yeasts_row, 8, item);
+
+    calcYeast();
+    is_changed();
+}
+
+
+void EditProduct::yeast_instock_changed(bool val)
+{
+    QSqlQuery query;
+
+    qDebug() << "yeast_instock_changed()" << product->yeasts_row << val;
+
+    this->yselectEdit->setCurrentIndex(-1);
+    this->yselectEdit->clear();
+    QString sql = "SELECT name,laboratory,product_id,inventory FROM inventory_yeasts ";
+    if (val)
+        sql.append("WHERE inventory > 0 ");
+    sql.append("ORDER BY laboratory,product_id,name");
+    query.prepare(sql);
+    query.exec();
+    query.first();
+    this->yselectEdit->addItem("");      // Start with empty value
+    for (int i = 0; i < query.size(); i++) {
+        this->yselectEdit->addItem(query.value(1).toString()+" - "+query.value(2).toString()+" "+query.value(0).toString() + 
+                        QString(" (%1 gr)").arg(query.value(3).toDouble() * 1000.0, 2, 'f', 1, '0'));
+        query.next();
+    }
+}
+
+
+void EditProduct::yeast_useat_changed(int val)
+{
+    qDebug() << "yeast_useat_changed()" << product->yeasts_row << val;
+
+    product->yeasts[product->yeasts_row].y_use = val;
+    QTableWidgetItem *item = new QTableWidgetItem(yeast_use[val]);
+    item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+    ui->yeastsTable->setItem(product->yeasts_row, 5, item);
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::editYeastRow_clicked()
+{
+    QSqlQuery query;
+
+    if (product->locked)
+	return;
+
+    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
+    product->yeasts_row = pb->objectName().toInt();
+    qDebug() << "Edit yeast row" << product->yeasts_row;
+    Yeasts backup = product->yeasts.at(product->yeasts_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("Yeast name:"));
+    nameLabel->setGeometry(QRect(10, 10, 141, 20));
+    nameLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    QLabel *laboratoryLabel = new QLabel(dialog);
+    laboratoryLabel->setObjectName(QString::fromUtf8("laboratoryLabel"));
+    laboratoryLabel->setText(tr("Laboratory:"));
+    laboratoryLabel->setGeometry(QRect(10, 40, 141, 20));
+    laboratoryLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    QLabel *product_idLabel = new QLabel(dialog);
+    product_idLabel->setObjectName(QString::fromUtf8("product_idLabel"));
+    product_idLabel->setText(tr("Laboratory:"));
+    product_idLabel->setGeometry(QRect(10, 70, 141, 20));
+    product_idLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    QLabel *selectLabel = new QLabel(dialog);
+    selectLabel->setObjectName(QString::fromUtf8("selectLabel"));
+    selectLabel->setText(tr("Select yeast:"));
+    selectLabel->setGeometry(QRect(10,100, 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,100, 121, 20));
+    instockLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    yamountLabel = new QLabel(dialog);
+    yamountLabel->setObjectName(QString::fromUtf8("amountLabel"));
+    if (product->yeasts.at(product->yeasts_row).y_form == 0)
+	yamountLabel->setText(tr("Total packs:"));
+    else if (product->yeasts.at(product->yeasts_row).y_form == 1)
+	yamountLabel->setText(tr("Amount in gr:"));
+    else
+	yamountLabel->setText(tr("Amount in ml:"));
+    yamountLabel->setGeometry(QRect(10, 130, 141, 20));
+    yamountLabel->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);
+
+    ynameEdit = new QLineEdit(dialog);
+    ynameEdit->setObjectName(QString::fromUtf8("ynameEdit"));
+    ynameEdit->setText(product->yeasts.at(product->yeasts_row).y_name);
+    ynameEdit->setGeometry(QRect(160, 10, 511, 23));
+    ynameEdit->setReadOnly(true);
+    ylaboratoryEdit = new QLineEdit(dialog);
+    ylaboratoryEdit->setObjectName(QString::fromUtf8("ylaboratoryEdit"));
+    ylaboratoryEdit->setText(product->yeasts.at(product->yeasts_row).y_laboratory);
+    ylaboratoryEdit->setGeometry(QRect(160, 40, 511, 23));
+    ylaboratoryEdit->setReadOnly(true);
+    yproduct_idEdit = new QLineEdit(dialog);
+    yproduct_idEdit->setObjectName(QString::fromUtf8("yproduct_idEdit"));
+    yproduct_idEdit->setText(product->yeasts.at(product->yeasts_row).y_product_id);
+    yproduct_idEdit->setGeometry(QRect(160, 70, 511, 23));
+    yproduct_idEdit->setReadOnly(true);
+    yselectEdit = new QComboBox(dialog);
+    yselectEdit->setObjectName(QString::fromUtf8("selectEdit"));
+    yselectEdit->setGeometry(QRect(160,100, 371, 23));
+    yinstockEdit = new QCheckBox(dialog);
+    yinstockEdit->setObjectName(QString::fromUtf8("yinstockEdit"));
+    yinstockEdit->setGeometry(QRect(655,100, 85, 21));
+    yinstockEdit->setChecked(true);
+    yamountEdit = new QDoubleSpinBox(dialog);
+    yamountEdit->setObjectName(QString::fromUtf8("yamountEdit"));
+    yamountEdit->setGeometry(QRect(160, 130, 121, 24));
+    yamountEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    yamountEdit->setAccelerated(true);
+    if (product->yeasts.at(product->yeasts_row).y_form == 0) {
+	yamountEdit->setDecimals(0);
+        yamountEdit->setSingleStep(1.0);
+	yamountEdit->setValue(product->yeasts.at(product->yeasts_row).y_amount);
+    } else if (product->yeasts.at(product->yeasts_row).y_form == 1) {
+    	yamountEdit->setDecimals(1);
+    	yamountEdit->setSingleStep(0.5);
+	yamountEdit->setValue(product->yeasts.at(product->yeasts_row).y_amount * 1000.0);
+    } else {
+	yamountEdit->setDecimals(1);
+        yamountEdit->setSingleStep(0.5);
+	yamountEdit->setValue(product->yeasts.at(product->yeasts_row).y_amount * 1000.0);
+    }
+    yamountEdit->setMaximum(1000000000.0);
+    useatEdit = new QComboBox(dialog);
+    useatEdit->setObjectName(QString::fromUtf8("useatEdit"));
+    useatEdit->setGeometry(QRect(160, 160, 161, 23));
+    useatEdit->addItem(tr("Primary"));
+    useatEdit->addItem(tr("Secondary"));
+    useatEdit->addItem(tr("Tertiary"));
+    useatEdit->addItem(tr("Bottle"));
+    useatEdit->setCurrentIndex(product->yeasts.at(product->yeasts_row).y_use);
+
+    yeast_instock_changed(true);
+
+    connect(yselectEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::yeast_select_changed);
+    connect(yamountEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::yeast_amount_changed);
+    connect(useatEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::yeast_useat_changed);
+    connect(yinstockEdit, &QCheckBox::stateChanged, this, &EditProduct::yeast_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->yeasts[product->yeasts_row] = backup;
+    } else {
+
+    }
+
+    disconnect(yselectEdit, nullptr, nullptr, nullptr);
+    disconnect(yamountEdit, nullptr, nullptr, nullptr);
+    disconnect(useatEdit, nullptr, nullptr, nullptr);
+    disconnect(yinstockEdit, nullptr, nullptr, nullptr);
+    disconnect(buttonBox, nullptr, nullptr, nullptr);
+
+    emit refreshAll();
+}
+
+
+void EditProduct::adjustYeasts(double factor)
+{
+    double amount;
+
+    if (product->yeasts.size() == 0)
+	return;
+
+    for (int i = 0; i < product->yeasts.size(); i++) {
+	if (product->yeasts.at(i).y_form == 1) { // Only adjust dry yeast
+	    amount = product->yeasts.at(i).y_amount * factor;
+	    product->yeasts[i].y_amount = amount;
+	}
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EditProductTab7.cpp	Thu Apr 28 22:49:13 2022 +0200
@@ -0,0 +1,682 @@
+/**
+ * EditProduct.cpp is part of bmsapp.
+ *
+ * tab 7, mash.
+ *
+ * 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/>.
+ */
+
+
+void EditProduct::refreshMashs()
+{
+    QString w;
+    QWidget* pWidget;
+    QHBoxLayout* pLayout;
+    QTableWidgetItem *item;
+    QIcon down_icon, up_icon;
+
+    qDebug() << "refreshMashs" << product->mashs.size();
+
+    down_icon.addFile(QString::fromUtf8(":/icons/silk/bullet_arrow_down.png"), QSize(), QIcon::Normal, QIcon::Off);
+    up_icon.addFile(QString::fromUtf8(":/icons/silk/bullet_arrow_up.png"), QSize(), QIcon::Normal, QIcon::Off);
+
+    const QStringList labels({tr("Step name"), tr("Type"), tr("Start"), tr("End"), tr("Rest"), tr("Ramp"),
+			      tr("Inf/dec"), tr("Inf/dec"), tr("Volume"), tr("W/G ratio"), "", "", tr("Delete"), tr("Edit") });
+
+    ui->mashsTable->setColumnCount(14);
+    ui->mashsTable->setColumnWidth(0, 189);	/* Step name	*/
+    ui->mashsTable->setColumnWidth(1, 100);	/* Type		*/
+    ui->mashsTable->setColumnWidth(2,  70);	/* Start temp	*/
+    ui->mashsTable->setColumnWidth(3,  70);	/* End temp	*/
+    ui->mashsTable->setColumnWidth(4,  70);	/* Rest time	*/
+    ui->mashsTable->setColumnWidth(5,  70);	/* Ramp time	*/
+    ui->mashsTable->setColumnWidth(6,  70);	/* Infusion vol	*/
+    ui->mashsTable->setColumnWidth(7,  70);     /* Infusion tmp	*/
+    ui->mashsTable->setColumnWidth(8,  70);	/* Volume	*/
+    ui->mashsTable->setColumnWidth(9,  80);	/* W/G ratio	*/
+    ui->mashsTable->setColumnWidth(10, 30);	/* Up button	*/
+    ui->mashsTable->setColumnWidth(11, 30);	/* Down button	*/
+    ui->mashsTable->setColumnWidth(12, 80);	/* Delete	*/
+    ui->mashsTable->setColumnWidth(13, 80);	/* Edit		*/
+    ui->mashsTable->setHorizontalHeaderLabels(labels);
+    ui->mashsTable->verticalHeader()->hide();
+    ui->mashsTable->setRowCount(product->mashs.size());
+
+    for (int i = 0; i < product->mashs.size(); i++) {
+
+	ui->mashsTable->setItem(i, 0, new QTableWidgetItem(product->mashs.at(i).step_name));
+
+	item = new QTableWidgetItem(step_types[product->mashs.at(i).step_type]);
+        item->setTextAlignment(Qt::AlignCenter|Qt::AlignVCenter);
+        ui->mashsTable->setItem(i, 1, item);
+
+	item = new QTableWidgetItem(QString("%1 °C").arg(product->mashs.at(i).step_temp, 2, 'f', 1, '0'));
+	item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+	ui->mashsTable->setItem(i, 2, item);
+
+	item = new QTableWidgetItem(QString("%1 °C").arg(product->mashs.at(i).end_temp, 2, 'f', 1, '0'));
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->mashsTable->setItem(i, 3, item);
+
+	item = new QTableWidgetItem(QString("%1 min").arg(product->mashs.at(i).step_time, 1, 'f', 0, '0'));
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->mashsTable->setItem(i, 4, item);
+
+	item = new QTableWidgetItem(QString("%1 min").arg(product->mashs.at(i).ramp_time, 1, 'f', 0, '0'));
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->mashsTable->setItem(i, 5, item);
+
+	if (product->mashs.at(i).step_infuse_amount > 0) {
+	    item = new QTableWidgetItem(QString("%1 L").arg(product->mashs.at(i).step_infuse_amount, 2, 'f', 1, '0'));
+            item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+            ui->mashsTable->setItem(i, 6, item);
+	    item = new QTableWidgetItem(QString("%1 °C").arg(product->mashs.at(i).step_infuse_temp, 3, 'f', 2, '0'));
+            item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+            ui->mashsTable->setItem(i, 7, item);
+	} else {
+	    ui->mashsTable->removeCellWidget(i, 6);
+	    ui->mashsTable->removeCellWidget(i, 7);
+	}
+
+	item = new QTableWidgetItem(QString("%1 L").arg(product->mashs.at(i).step_volume, 2, 'f', 1, '0'));
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->mashsTable->setItem(i, 8, item);
+
+	item = new QTableWidgetItem(QString("%1 L/kg").arg(product->mashs.at(i).step_wg_ratio, 3, 'f', 2, '0'));
+        item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+        ui->mashsTable->setItem(i, 9, item);
+
+	if (i > 0) {
+	    pWidget = new QWidget();
+            QPushButton* btn_up = new QPushButton();
+            btn_up->setObjectName(QString("%1").arg(i));  /* Send row with the button */
+            btn_up->setIcon(up_icon);
+            connect(btn_up, SIGNAL(clicked()), this, SLOT(upMashRow_clicked()));
+            pLayout = new QHBoxLayout(pWidget);
+            pLayout->addWidget(btn_up);
+            pLayout->setContentsMargins(5, 0, 5, 0);
+            pWidget->setLayout(pLayout);
+            ui->mashsTable->setCellWidget(i, 10, pWidget);
+	} else {
+	    ui->mashsTable->removeCellWidget(i, 10);
+	}
+
+	if (i < (product->mashs.size() - 1)) {
+	    pWidget = new QWidget();
+            QPushButton* btn_down = new QPushButton();
+            btn_down->setObjectName(QString("%1").arg(i));  /* Send row with the button */
+	    btn_down->setIcon(down_icon);
+            connect(btn_down, SIGNAL(clicked()), this, SLOT(downMashRow_clicked()));
+            pLayout = new QHBoxLayout(pWidget);
+            pLayout->addWidget(btn_down);
+            pLayout->setContentsMargins(5, 0, 5, 0);
+            pWidget->setLayout(pLayout);
+            ui->mashsTable->setCellWidget(i, 11, pWidget);
+	} else {
+	    ui->mashsTable->removeCellWidget(i, 11);
+	}
+
+	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(deleteMashRow_clicked()));
+        pLayout = new QHBoxLayout(pWidget);
+        pLayout->addWidget(btn_dele);
+        pLayout->setContentsMargins(5, 0, 5, 0);
+        pWidget->setLayout(pLayout);
+        ui->mashsTable->setCellWidget(i, 12, 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(editMashRow_clicked()));
+        pLayout = new QHBoxLayout(pWidget);
+        pLayout->addWidget(btn_edit);
+        pLayout->setContentsMargins(5, 0, 5, 0);
+        pWidget->setLayout(pLayout);
+        ui->mashsTable->setCellWidget(i, 13, pWidget);
+    }
+}
+
+
+double EditProduct::infusionVol(double step_infused, double step_mashkg, double infuse_temp, double step_temp, double last_temp)
+{
+    double a = last_temp * (equip_tun_weight * equip_tun_specific_heat + step_infused * SpecificHeatWater + step_mashkg * SpecificHeatMalt);
+    double b = step_temp * (equip_tun_weight * equip_tun_specific_heat + step_infused * SpecificHeatWater + step_mashkg * SpecificHeatMalt);
+    double vol = round(((b - a) / ((infuse_temp - step_temp) * SpecificHeatWater)) * 100.0) / 100.0;
+
+    if (vol < 0)
+	vol = 0;
+    qDebug() << "  infusionVol(" << step_infused << "," << step_mashkg << "," << infuse_temp <<"," << step_temp << "," << last_temp << "):" << vol;
+    return vol;
+}
+
+
+double EditProduct::decoctionVol(double step_volume, double step_temp, double prev_temp)
+{
+    double a = (equip_tun_weight * equip_tun_specific_heat + step_volume * SpecificHeatWater) * (step_temp - prev_temp);
+    double b = SpecificHeatWater * (99 - step_temp);
+    double vol = 0;
+
+    if (b > 0)
+	vol = round((a / b) * 1000000.0) / 1000000.0;
+    qDebug() << "  decoctionVol(" << step_volume << "," << step_temp << "," << prev_temp << "):" << vol;
+    return vol;
+}
+
+
+void EditProduct::calcMash()
+{
+    double infused = 0, vol, a, b, temp;
+    int    i, j, n;
+    double lasttemp = 18.0;
+    double graintemp = 18.0;
+    double tuntemp = 18.0;
+
+    product->mashs_time = 0;
+
+    if (product->mashs.size() && product->mashs_kg > 0) {
+	qDebug() << "calcMash()";
+
+	for (i = 0; i < product->mashs.size(); i++) {
+	    if (product->mashs.at(i).step_type == 0) { // Infusion
+		if (i == 0) {
+		    // First mash step, temperature from the mashtun and malt.
+		    n = 20; // tun is preheated.
+		    tuntemp = product->mashs.at(i).step_temp;
+		    for (j = 0; j < n; j++) {
+			a = product->mashs_kg * graintemp * SpecificHeatMalt + equip_tun_weight * tuntemp * equip_tun_specific_heat;
+			b = product->mashs[i].step_temp *
+			      (equip_tun_weight * equip_tun_specific_heat +
+			       product->mashs.at(i).step_infuse_amount * SpecificHeatWater + 
+			       product->mashs_kg * SpecificHeatMalt) -
+			      SlakingHeat * product->mashs_kg;
+			if (product->mashs.at(i).step_infuse_amount > 0) {
+			    temp = (b - a) / (product->mashs.at(i).step_infuse_amount * SpecificHeatWater);
+			} else {
+			    temp = 99;
+			}
+			tuntemp += (temp - tuntemp) / 2;
+			product->mashs[i].step_infuse_temp = round(temp * 1000000.0) / 1000000.0;
+		    }
+		    qDebug() << "  init infuse temp:" << product->mashs.at(i).step_infuse_temp;
+		} else {
+		    // Calculate amount of infusion water.
+		    product->mashs[i].step_infuse_amount =
+			    infusionVol(infused, product->mashs_kg, product->mashs.at(i).step_infuse_temp, product->mashs.at(i).step_temp, lasttemp);
+		    qDebug() << i << "  vol:" << product->mashs.at(i).step_infuse_amount << "temp:" << product->mashs.at(i).step_infuse_temp;
+		}
+		infused += product->mashs.at(i).step_infuse_amount;
+	    } else if (product->mashs.at(i).step_type == 1) { // Temperature
+		if (i > 0)
+		    product->mashs[i].step_infuse_amount = 0;
+		product->mashs[i].step_infuse_temp = 0;
+	    } else if (product->mashs.at(i).step_type == 2) { // Decoction
+		product->mashs[i].step_infuse_amount = decoctionVol(infused, product->mashs.at(i).step_temp, lasttemp);
+		product->mashs[i].step_infuse_temp = 99;
+	    }
+	    product->mashs[i].step_volume = infused;
+	    //qDebug() << i << "  type:" << product->mashs.at(i).step_type << "volume:" << product->mashs.at(i).step_infuse_amount << "temp:" << product->mashs.at(i).step_infuse_temp;
+	    lasttemp = product->mashs.at(i).step_temp;
+	    product->mashs_time += product->mashs.at(i).step_time;
+	    if (i > 0)
+	    	product->mashs_time += product->mashs.at(i).ramp_time;
+	    product->mashs[i].step_wg_ratio = round((infused / product->mashs_kg) * 1000000.0) / 1000000.0;
+	}
+    }
+
+    /* Show the calculated total mash time. */
+    ui->mash_timeEdit->setText(QString("%1:%2").arg(product->mashs_time / 60).arg(product->mashs_time % 60, 2, 'f', 0, '0'));
+}
+
+
+void EditProduct::addMashRow_clicked()
+{
+    Mashs newm;
+
+    for (int i = 0; i < product->mashs.size(); i++) {
+        if (product->mashs.at(i).step_time == 0)
+            return;     // Add only one at a time.
+    }
+
+    newm.step_name = "Name me";
+    newm.step_temp = newm.end_temp = 67.0;
+    newm.step_time = 20;
+    newm.ramp_time = 10;
+    if (product->mashs.size()) {
+	newm.step_volume = product->mashs.at(product->mashs.size() - 1).step_volume;
+	newm.step_wg_ratio = product->mashs.at(product->mashs.size() - 1).step_wg_ratio;
+	newm.step_type = product->mashs.at(product->mashs.size() - 1).step_type;
+    } else {
+	newm.step_volume = 0;
+	newm.step_wg_ratio = 0;
+	newm.step_type = 1;
+    }
+    newm.step_infuse_amount = newm.step_infuse_temp = 0;
+
+    product->mashs.append(newm);
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::deleteMashRow_clicked()
+{
+    if (product->locked || product->mashs.size() < 1)
+	return;
+
+    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
+    int row = pb->objectName().toInt();
+    qDebug() << "Delete mash row" << row << product->mashs.size();
+
+    int rc = QMessageBox::warning(this, tr("Delete mash step"), tr("Delete %1").arg(product->mashs.at(row).step_name),
+                    QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
+    if (rc == QMessageBox::No)
+        return;
+
+    product->mashs.removeAt(row);
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::upMashRow_clicked()
+{
+    if (product->locked || product->mashs.size() < 1)
+        return;
+
+    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
+    int row = pb->objectName().toInt();
+    qDebug() << "Move up mash row" << row << product->mashs.size();
+
+    Mashs temp;
+    temp = product->mashs[row - 1];
+    product->mashs[row - 1] = product->mashs[row];
+    product->mashs[row] = temp;
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::downMashRow_clicked()
+{
+    if (product->locked || product->mashs.size() < 1)
+        return;
+
+    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
+    int row = pb->objectName().toInt();
+    qDebug() << "Move down mash row" << row << product->mashs.size();
+
+    Mashs temp;
+    temp = product->mashs[row + 1];
+    product->mashs[row + 1] = product->mashs[row];
+    product->mashs[row] = temp;
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::step_name_changed(QString val)
+{
+    product->mashs[product->mashs_row].step_name = val;
+    ui->mashsTable->setItem(product->mashs_row, 0, new QTableWidgetItem(val));
+    is_changed();
+}
+
+
+void EditProduct::step_type_changed(int val)
+{
+    qDebug() << "step_type_changed" << product->mashs_row << val;
+
+    product->mashs[product->mashs_row].step_type = val;
+    ivolLabel->setVisible(product->mashs.at(product->mashs_row).step_type == 0);
+    stepivolEdit->setVisible(product->mashs.at(product->mashs_row).step_type == 0);
+    itmpLabel->setVisible(product->mashs.at(product->mashs_row).step_type == 0);
+    stepitmpEdit->setVisible(product->mashs.at(product->mashs_row).step_type == 0);
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::step_temp_changed(double val)
+{
+    qDebug() << "step_temp_changed" << product->mashs_row << val;
+    product->mashs[product->mashs_row].step_temp = val;
+    QTableWidgetItem *item = new QTableWidgetItem(QString("%1 °C").arg(val, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->mashsTable->setItem(product->mashs_row, 2, item);
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::end_temp_changed(double val)
+{
+    qDebug() << "end_temp_changed" << product->mashs_row << val;
+    product->mashs[product->mashs_row].end_temp = val;
+    QTableWidgetItem *item = new QTableWidgetItem(QString("%1 °C").arg(val, 2, 'f', 1, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->mashsTable->setItem(product->mashs_row, 3, item);
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::step_time_changed(double val)
+{
+    qDebug() << "step_time_changed" << product->mashs_row << val;
+    product->mashs[product->mashs_row].step_time = val;
+    QTableWidgetItem *item = new QTableWidgetItem(QString("%1 min").arg(val, 1, 'f', 0, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->mashsTable->setItem(product->mashs_row, 4, item);
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::ramp_time_changed(double val)
+{
+    qDebug() << "ramp_time_changed" << product->mashs_row << val;
+    product->mashs[product->mashs_row].ramp_time = val;
+    QTableWidgetItem *item = new QTableWidgetItem(QString("%1 min").arg(val, 1, 'f', 0, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->mashsTable->setItem(product->mashs_row, 5, item);
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::infuse_changed(double val)
+{
+    qDebug() << "infuse_changed" << product->mashs_row << val;
+    product->mashs[product->mashs_row].step_infuse_amount = val;
+    QTableWidgetItem *item = new QTableWidgetItem(QString("%1 L").arg(val, 1, 'f', 0, '0'));
+    item->setTextAlignment(Qt::AlignRight|Qt::AlignVCenter);
+    ui->mashsTable->setItem(product->mashs_row, 6, item);
+
+    /*
+     * Recalculate water volumes
+     */
+    double volume = 0;
+    for (int i = 0; i < product->mashs.size(); i++) {
+	if (product->mashs.at(i).step_type == 0) {
+	    volume += product->mashs.at(i).step_infuse_amount;
+	}
+	product->mashs[i].step_volume = volume;
+    }
+    product->w1_amount = volume - product->w2_amount;
+    product->wg_amount = volume;
+    ui->w1_volEdit->setValue(product->w1_amount);
+
+    is_changed();
+    emit refreshAll();
+}
+
+
+void EditProduct::editMashRow_clicked()
+{
+    QSqlQuery query;
+
+    if (product->locked)
+	return;
+
+    QPushButton *pb = qobject_cast<QPushButton *>(QObject::sender());
+    product->mashs_row = pb->objectName().toInt();
+    qDebug() << "Edit mash row" << product->mashs_row;
+    Mashs backup = product->mashs.at(product->mashs_row);
+
+    QDialog* dialog = new QDialog(this);
+    dialog->resize(738, 230);
+    QDialogButtonBox *buttonBox = new QDialogButtonBox(dialog);
+    buttonBox->setObjectName(QString::fromUtf8("buttonBox"));
+    buttonBox->setGeometry(QRect(30, 180, 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("Step name:"));
+    nameLabel->setGeometry(QRect(10, 10, 141, 20));
+    nameLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    stepnameEdit = new QLineEdit(dialog);
+    stepnameEdit->setObjectName(QString::fromUtf8("stepnameEdit"));
+    stepnameEdit->setText(product->mashs.at(product->mashs_row).step_name);
+    stepnameEdit->setGeometry(QRect(160, 10, 511, 23));
+
+    QLabel *typeLabel = new QLabel(dialog);
+    typeLabel->setObjectName(QString::fromUtf8("typeLabel"));
+    typeLabel->setText(tr("Step type:"));
+    typeLabel->setGeometry(QRect(10, 40, 141, 20));
+    typeLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    QComboBox *typeEdit = new QComboBox(dialog);
+    typeEdit->setObjectName(QString::fromUtf8("typeEdit"));
+    typeEdit->setGeometry(QRect(160, 40, 161, 23));
+    typeEdit->addItem(tr("Infusion"));
+    typeEdit->addItem(tr("Temperature"));
+    typeEdit->addItem(tr("Decoction"));
+    typeEdit->setCurrentIndex(product->mashs.at(product->mashs_row).step_type);
+
+    QLabel *tempLabel = new QLabel(dialog);
+    tempLabel->setObjectName(QString::fromUtf8("tempLabel"));
+    tempLabel->setText(tr("Step start temp:"));
+    tempLabel->setGeometry(QRect(10, 70, 141, 20));
+    tempLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    steptempEdit = new QDoubleSpinBox(dialog);
+    steptempEdit->setObjectName(QString::fromUtf8("steptempEdit"));
+    steptempEdit->setGeometry(QRect(160, 70, 121, 24));
+    steptempEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    steptempEdit->setDecimals(1);
+    steptempEdit->setValue(product->mashs.at(product->mashs_row).step_temp);
+
+    QLabel *endLabel = new QLabel(dialog);
+    endLabel->setObjectName(QString::fromUtf8("endLabel"));
+    endLabel->setText(tr("Step end temp:"));
+    endLabel->setGeometry(QRect(360, 70, 141, 20));
+    endLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    endtempEdit = new QDoubleSpinBox(dialog);
+    endtempEdit->setObjectName(QString::fromUtf8("endtempEdit"));
+    endtempEdit->setGeometry(QRect(510, 70, 121, 24));
+    endtempEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    endtempEdit->setDecimals(1);
+    endtempEdit->setValue(product->mashs.at(product->mashs_row).end_temp);
+
+    QLabel *timeLabel = new QLabel(dialog);
+    timeLabel->setObjectName(QString::fromUtf8("timeLabel"));
+    timeLabel->setText(tr("Step rest time:"));
+    timeLabel->setGeometry(QRect(10, 100, 141, 20));
+    timeLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    steptimeEdit = new QDoubleSpinBox(dialog);
+    steptimeEdit->setObjectName(QString::fromUtf8("steptimeEdit"));
+    steptimeEdit->setGeometry(QRect(160, 100, 121, 24));
+    steptimeEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    steptimeEdit->setDecimals(0);
+    steptimeEdit->setValue(product->mashs.at(product->mashs_row).step_time);
+
+    QLabel *rampLabel = new QLabel(dialog);
+    rampLabel->setObjectName(QString::fromUtf8("rampLabel"));
+    rampLabel->setText(tr("Step ramp time:"));
+    rampLabel->setGeometry(QRect(360, 100, 141, 20));
+    rampLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    ramptimeEdit = new QDoubleSpinBox(dialog);
+    ramptimeEdit->setObjectName(QString::fromUtf8("ramptimeEdit"));
+    ramptimeEdit->setGeometry(QRect(510, 100, 121, 24));
+    ramptimeEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    ramptimeEdit->setDecimals(0);
+    ramptimeEdit->setValue(product->mashs.at(product->mashs_row).ramp_time);
+
+    /*
+     * Only used for Infusion steps.
+     */
+    ivolLabel = new QLabel(dialog);
+    ivolLabel->setObjectName(QString::fromUtf8("ivolLabel"));
+    ivolLabel->setText(tr("Infusion volume:"));
+    ivolLabel->setGeometry(QRect(10, 130, 141, 20));
+    ivolLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    ivolLabel->setVisible(product->mashs.at(product->mashs_row).step_type == 0);
+    stepivolEdit = new QDoubleSpinBox(dialog);
+    stepivolEdit->setObjectName(QString::fromUtf8("stepivolEdit"));
+    stepivolEdit->setGeometry(QRect(160, 130, 121, 24));
+    stepivolEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    stepivolEdit->setVisible(product->mashs.at(product->mashs_row).step_type == 0);
+    stepivolEdit->setDecimals(1);
+    stepivolEdit->setAccelerated(true);
+    stepivolEdit->setMaximum(100000.0);
+    stepivolEdit->setSingleStep(0.5);
+    stepivolEdit->setValue(product->mashs.at(product->mashs_row).step_infuse_amount);
+
+    itmpLabel = new QLabel(dialog);
+    itmpLabel->setObjectName(QString::fromUtf8("itmpLabel"));
+    itmpLabel->setText(tr("Infusion Temperature:"));
+    itmpLabel->setGeometry(QRect(360, 130, 141, 20));
+    itmpLabel->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    itmpLabel->setVisible(product->mashs.at(product->mashs_row).step_type == 0);
+    stepitmpEdit = new QDoubleSpinBox(dialog);
+    stepitmpEdit->setObjectName(QString::fromUtf8("stepitmpEdit"));
+    stepitmpEdit->setGeometry(QRect(510, 130, 121, 24));
+    stepitmpEdit->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
+    stepitmpEdit->setVisible(product->mashs.at(product->mashs_row).step_type == 0);
+    stepitmpEdit->setDecimals(1);
+    stepitmpEdit->setReadOnly(true);
+    stepitmpEdit->setButtonSymbols(QAbstractSpinBox::NoButtons);
+    stepitmpEdit->setValue(product->mashs.at(product->mashs_row).step_infuse_temp);
+
+    connect(stepnameEdit, &QLineEdit::textEdited, this, &EditProduct::step_name_changed);
+    connect(typeEdit, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &EditProduct::step_type_changed);
+    connect(steptempEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::step_temp_changed);
+    connect(endtempEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::end_temp_changed);
+    connect(steptimeEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::step_time_changed);
+    connect(ramptimeEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::ramp_time_changed);
+    connect(stepivolEdit, QOverload<double>::of(&QDoubleSpinBox::valueChanged), this, &EditProduct::infuse_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->mashs[product->mashs_row] = backup;
+	/* Rollback water volumes too */
+	double volume = 0;
+    	for (int i = 0; i < product->mashs.size(); i++) {
+            if (product->mashs.at(i).step_type == 0) {
+            	volume += product->mashs.at(i).step_infuse_amount;
+            }
+            product->mashs[i].step_volume = volume;
+    	}
+    	product->w1_amount = volume - product->w2_amount;
+    	product->wg_amount = volume;
+    	ui->w1_volEdit->setValue(product->w1_amount);
+    }
+
+    disconnect(stepnameEdit, nullptr, nullptr, nullptr);
+    disconnect(steptempEdit, nullptr, nullptr, nullptr);
+    disconnect(endtempEdit, nullptr, nullptr, nullptr);
+    disconnect(steptimeEdit, nullptr, nullptr, nullptr);
+    disconnect(ramptimeEdit, nullptr, nullptr, nullptr);
+    disconnect(stepivolEdit, nullptr, nullptr, nullptr);
+    disconnect(buttonBox, nullptr, nullptr, nullptr);
+
+    emit refreshAll();
+}
+
+
+void EditProduct::mash_name_changed(QString name)
+{
+    qDebug() << "mash_name_changed" << name;
+    product->mash_name = name;
+    is_changed();
+}
+
+
+void EditProduct::mash_select_changed(int val)
+{
+    QSqlQuery query;
+    int  i;
+
+    qDebug() << "mash_select_changed" << val;
+
+    const QSignalBlocker blocker1(ui->mash_nameEdit);
+    query.prepare("SELECT name,steps FROM profile_mash ORDER BY name");
+    query.exec();
+    query.first();
+    for (i = 0; i < (val -1); i++) {
+	query.next();
+    }
+
+    product->mash_name = query.value(0).toString();
+    ui->mash_nameEdit->setText(product->mash_name);
+
+    QJsonParseError parseError;
+    const auto& json = query.value(1).toString();
+    if (!json.trimmed().isEmpty()) {
+	const auto& formattedJson = QString("%1").arg(json);
+	QJsonDocument newsteps = QJsonDocument::fromJson(formattedJson.toUtf8(), &parseError);
+
+	if (parseError.error != QJsonParseError::NoError) {
+	    qDebug() << "Parse error: " << parseError.errorString() << "at" << parseError.offset;
+	} else {
+	    /*
+	     * Got the json data in the steps array, replace the product steps.
+	     */
+	    double infuse = 0;
+	    if (product->mashs.size()) {
+	    	infuse = product->mashs.at(0).step_infuse_amount;
+	    	product->mashs.clear();
+	    	ui->mashsTable->clear();
+	    }
+	    if (newsteps.isArray()) {
+		for (i = 0; i < newsteps.array().size(); i++) {
+		    QJsonObject obj = newsteps.array().at(i).toObject();
+		    Mashs m;
+		    m.step_name = obj["step_name"].toString();
+		    if (obj["step_type"].isString())
+			m.step_type = QString(obj["step_type"].toString()).toInt();
+		    else
+		        m.step_type = obj["step_type"].toInt();
+		    m.step_volume = 0;
+		    m.step_infuse_amount = 0;
+		    m.step_infuse_temp = 0;
+		    if (obj["step_temp"].isString())
+			m.step_temp = QString(obj["step_temp"].toString()).toDouble();
+		    else
+		        m.step_temp = obj["step_temp"].toDouble();
+		    if (obj["step_time"].isString())
+			m.step_time = QString(obj["step_time"].toString()).toDouble();
+		    else
+		        m.step_time = obj["step_time"].toDouble();
+		    if (obj["ramp_time"].isString())
+			m.ramp_time = QString(obj["ramp_time"].toString()).toDouble();
+		    else
+		        m.ramp_time = obj["ramp_time"].toDouble();
+		    if (obj["end_temp"].isString())
+			m.end_temp = QString(obj["end_temp"].toString()).toDouble();
+		    else
+		        m.end_temp = obj["end_temp"].toDouble();
+		    m.step_wg_ratio = 0;
+		    product->mashs.append(m);
+		}
+	    }
+	    if (product->mashs.at(0).step_type == 0)
+		product->mashs[0].step_infuse_amount = infuse;	// Restore saved initial infusion
+	}
+    }
+    is_changed();
+    emit refreshAll();
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EditProductTab8.cpp	Thu Apr 28 22:49:13 2022 +0200
@@ -0,0 +1,797 @@
+/**
+ * EditProduct.cpp is part of bmsapp.
+ *
+ * tab 8, water.
+ *
+ * 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/>.
+ */
+
+
+void EditProduct::refreshWaters()
+{
+
+    // calc_acid
+    ui->mw_phEdit->setValue(product->mash_ph);
+    // mash_name
+    //ui->mw_acidpercEdit->setValue(product->
+
+}
+
+
+/*
+ * Z alkalinity is the amount of acid (in mEq/l) needed to bring water to the target pH (Z pH)
+ */
+double EditProduct::ZAlkalinity(double pHZ)
+{
+    double C43 = Utils::Charge(4.3);
+    double Cw = Utils::Charge(product->wg_ph);
+    double Cz = Utils::Charge(pHZ);
+    double DeltaCNaught = -C43 + Cw;
+    double CT = product->wg_total_alkalinity / 50 / DeltaCNaught;
+    double DeltaCZ = -Cz + Cw;
+    return CT * DeltaCZ;
+}
+
+
+/*
+ * Z Residual alkalinity is the amount of acid (in mEq/l) needed
+ * to bring the water in the mash to the target pH (Z pH).
+ */
+double EditProduct::ZRA(double pHZ)
+{
+    double Calc = product->wg_calcium / (MMCa / 2);
+    double Magn = product->wg_magnesium / (MMMg / 2);
+    double Z = ZAlkalinity(pHZ);
+    return Z - (Calc / 3.5 + Magn / 7);
+}
+
+
+double EditProduct::BufferCapacity(Fermentables F)
+{
+    double C1 = 0;
+
+    if ((F.f_di_ph != 5.7) && ((F.f_acid_to_ph_57 < - 0.1) || (F.f_acid_to_ph_57 > 0.1))) {
+	C1 = F.f_acid_to_ph_57 / (F.f_di_ph - 5.7);
+     } else {
+	/*
+	 * If the acid_to_ph_5.7 is unknown from the maltster, guess the required acid.
+	 */
+	switch (F.f_graintype) {
+	   case 0:					// Base, Special, Kilned
+	   case 3:
+	   case 5:	C1 = 0.014 * F.f_color - 34.192;
+			break;
+	   case 2:	C1 = -0.0597 * F.f_color - 32.457;	// Crystal
+			break;
+	   case 1:	C1 = 0.0107 * F.f_color - 54.768;	// Roast
+			break;
+	   case 4:	C1 = -149;                      // Sour malt
+			break;
+	}
+    }
+    return C1;
+}
+
+
+double EditProduct::AcidRequired(double ZpH, Fermentables F)
+{
+    double C1 = BufferCapacity(F);
+    double x = F.f_di_ph;
+    return C1 * (ZpH - x);
+}
+
+
+double EditProduct::ProtonDeficit(double pHZ)
+{
+    double C1, x;
+    int i, error_count = 0;
+    double Result = ZRA(pHZ) * product->wg_amount;
+    Fermentables F;
+
+    /*
+     * proton deficit for the grist
+     */
+    if (product->fermentables.size()) {
+	for (i = 0; i < product->fermentables.size(); i++) {
+	    F = product->fermentables.at(i);
+	    if (F.f_added == 0 && F.f_graintype != 6) { // Added == Mash && graintype != No Malt
+		x = AcidRequired(pHZ, F) * F.f_amount;
+		Result += x;
+	    }
+	}
+    } else {
+	error_count++;
+	if (error_count < 5)
+	   qDebug() << "  ProtonDeficit" <<  pHZ << "invalid grist, return" << Result;
+    }
+    return Result;
+}
+
+
+double EditProduct::MashpH()
+{
+    int n = 0;
+    double pH = 5.4;
+    double deltapH = 0.001;
+    double deltapd = 0.1;
+    double pd = ProtonDeficit(pH);
+
+    while (((pd < -deltapd) || (pd > deltapd)) && (n < 2000)) {
+	n++;
+	if (pd < -deltapd)
+	    pH -= deltapH;
+	else if (pd > deltapd)
+	    pH += deltapH;
+	pd = ProtonDeficit(pH);
+    }
+    pH = round(pH * 1000000) / 1000000.0;
+    qDebug() << "  MashpH() n:" << n << "pH:" << pH;
+    return pH;
+}
+
+
+void EditProduct::calcWater()
+{
+    double liters = 0;
+    double calcium = 0;
+    double magnesium = 0;
+    double sodium = 0;
+    double total_alkalinity = 0;
+    double bicarbonate = 0;
+    double chloride = 0;
+    double sulfate = 0;
+    double ph = 0;
+    double TpH = 0;
+    double frac, RA;
+    double protonDeficit = 0;
+    double Acid = 0, Acidmg = 0;
+    int AT;
+
+    qDebug() << "calcWater()";
+
+    /*
+     * If there is a dilute water source, mix the waters.
+     */
+    if (product->w2_name != "") {
+	liters = product->w1_amount + product->w2_amount;
+	calcium = Utils::mix(product->w1_amount, product->w2_amount, product->w1_calcium, product->w2_calcium);
+	magnesium = Utils::mix(product->w1_amount, product->w2_amount, product->w1_magnesium, product->w2_magnesium);
+	sodium = Utils::mix(product->w1_amount, product->w2_amount, product->w1_sodium, product->w2_sodium);
+	chloride = Utils::mix(product->w1_amount, product->w2_amount, product->w1_chloride, product->w2_chloride);
+	sulfate = Utils::mix(product->w1_amount, product->w2_amount, product->w1_sulfate, product->w2_sulfate);
+	total_alkalinity = Utils::mix(product->w1_amount, product->w2_amount, product->w1_total_alkalinity, product->w2_total_alkalinity);
+	ph = -log10(((pow(10, -product->w1_ph) * product->w1_amount) + (pow(10, -product->w2_ph) * product->w2_amount)) / liters);
+    } else {
+	liters = product->w1_amount;
+	calcium = product->w1_calcium;
+	magnesium = product->w1_magnesium;
+	sodium = product->w1_sodium;
+	chloride = product->w1_chloride;
+	sulfate = product->w1_sulfate;
+	total_alkalinity = product->w1_total_alkalinity;
+	ph = product->w1_ph;
+    }
+
+    product->wg_amount = liters;
+    product->wg_calcium = round(calcium * 10.0) / 10.0;
+    product->wg_magnesium = round(magnesium * 10.0) / 10.0;
+    product->wg_sodium = round(sodium * 10.0) / 10.0;
+    product->wg_chloride = round(chloride * 10.0) / 10.0;
+    product->wg_sulfate = round(sulfate * 10.0) / 10.0;
+    product->wg_total_alkalinity = round(total_alkalinity * 10.0) / 10.0;
+    product->wg_ph = ph;
+
+    ui->wg_volEdit->setValue(liters);
+    ui->wg_caEdit->setValue(calcium);
+    ui->wg_mgEdit->setValue(magnesium);
+    ui->wg_hco3Edit->setValue(total_alkalinity * 1.22);
+    ui->wg_caco3Edit->setValue(total_alkalinity);
+    ui->wg_naEdit->setValue(sodium);
+    ui->wg_clEdit->setValue(chloride);
+    ui->wg_so4Edit->setValue(sulfate);
+    ui->wg_phEdit->setValue(ph);
+    bicarbonate = total_alkalinity * 1.22;
+
+    /* Save mixed water ions for later */
+    double wg_calcium = calcium;
+    double wg_sodium = sodium;
+    double wg_total_alkalinity = total_alkalinity;
+    double wg_chloride = chloride;
+    double wg_sulfate = sulfate;
+    double wg_bicarbonate = bicarbonate;
+
+    double mash_ph = MashpH();
+    qDebug() << "  Distilled water mash pH:" << mash_ph;
+
+    /* Calculate Salt additions */
+   if (liters > 0) {
+	calcium += ( ui->bs_cacl2Edit->value() * MMCa / MMCaCl2 * 1000 + ui->bs_caso4Edit->value() * MMCa / MMCaSO4 * 1000 +
+		     ui->bs_caco3Edit->value() * MMCa / MMCaCO3 * 1000) / liters;
+	magnesium += (ui->bs_mgso4Edit->value() * MMMg / MMMgSO4 * 1000 + ui->bs_mgcl2Edit->value() * MMMg / MMMgCl2 * 1000) / liters;
+	sodium += (ui->bs_naclEdit->value() * MMNa / MMNaCl * 1000 + ui->bs_nahco3Edit->value() * MMNa / MMNaHCO3 * 1000) / liters;
+	sulfate += (ui->bs_caso4Edit->value() * MMSO4 / MMCaSO4 * 1000 + ui->bs_mgso4Edit->value() * MMSO4 / MMMgSO4 * 1000) / liters;
+	chloride += (2 * ui->bs_cacl2Edit->value() * MMCl / MMCaCl2 * 1000 + ui->bs_naclEdit->value() * MMCl / MMNaCl * 1000 +
+		     ui->bs_mgcl2Edit->value() * MMCl / MMMgCl2 * 1000) / liters;
+	bicarbonate += (ui->bs_nahco3Edit->value() * MMHCO3 / MMNaHCO3 * 1000 + ui->bs_caco3Edit->value() / 3 * MMHCO3 / MMCaCO3 * 1000) / liters;
+    }
+
+    const QSignalBlocker blocker1(ui->mw_acidPick);
+    const QSignalBlocker blocker2(ui->mw_acidpercEdit);
+    const QSignalBlocker blocker3(ui->mw_acidvolEdit);
+    const QSignalBlocker blocker4(ui->wb_phEdit);
+    const QSignalBlocker blocker5(ui->mw_phEdit);
+
+    if (product->wa_acid_name < 0 || product->wa_acid_name >= my_acids.size()) {
+	product->wa_acid_name = 0;
+	product->wa_acid_perc = my_acids.at(0).AcidPrc;
+	ui->mw_acidPick->setCurrentIndex(0);
+	ui->mw_acidpercEdit->setValue(my_acids.at(0).AcidPrc);
+    }
+    AT = product->wa_acid_name;
+
+    /*
+     * Note that the next calculations do not correct the pH change by the added salts.
+     * This pH change is at most 0.1 pH and is a minor difference in Acid amount.
+     */
+    if (product->calc_acid) {
+	/*
+	 * Auto calculate the needed acid.
+	 */
+	TpH = product->mash_ph;
+	protonDeficit = ProtonDeficit(TpH);
+	qDebug() << "  calc_acid tgt:" << TpH << "protonDeficit:" << protonDeficit;
+	if (protonDeficit > 0) {
+	    frac = Utils::CalcFrac(TpH, my_acids[AT].pK1, my_acids[AT].pK2, my_acids[AT].pK3);
+	    Acid = protonDeficit / frac;
+	    Acid *= my_acids[AT].MolWt;	// mg.
+	    Acidmg = Acid;
+	    Acid = Acid / my_acids[AT].AcidSG;
+	    Acid = round((Acid / (product->wa_acid_perc / 100.0)) * 100.0) / 100.0;
+	    qDebug() << "  Mash auto Acid final ml:" << Acid;
+
+	    QString w = my_acids[AT].name_en + ' ' + my_acids[AT].name_nl;
+	    brewing_salt_sub(w, Acid);
+	    ui->mw_acidvolEdit->setValue(Acid);
+
+	    bicarbonate = bicarbonate - protonDeficit * frac / liters;
+	    total_alkalinity = bicarbonate * 50 / 61;
+	}
+	ph = TpH;
+	ui->wb_phEdit->setValue(ph);
+	product->mash_ph = ph;
+    } else { // Manual
+	/*
+	 * Manual adjust acid, calculate resulting pH.
+	 */
+	double pHa = ph;	// Mixed water pH.
+	// Then calculate the new pH with added acids and malts
+	qDebug() << "  Mash pH:" << pHa;
+	Acid = my_acids[AT].AcidSG * (product->wa_acid_perc / 100.0);	// ml
+	Acid *= ui->mw_acidvolEdit->value();
+	Acid /= my_acids[AT].MolWt;	// mg;
+	Acidmg = Acid;
+
+	//find the pH where the protondeficit = protondeficit by the acid
+	frac = Utils::CalcFrac(pHa, my_acids[AT].pK1, my_acids[AT].pK2, my_acids[AT].pK3);
+	protonDeficit = Acid * frac;
+	//qDebug() << "  protonDeficit Acid:" << protonDeficit << "frac:" << frac << "pH:" << pHa;
+
+	double deltapH = 0.001;
+   	double deltapd = 0.1;
+   	double pd = round(ProtonDeficit(pHa) * 1000000.0) / 1000000.0;
+	int n = 0;
+	while (((pd < (protonDeficit - deltapd)) || (pd > (protonDeficit + deltapd))) && (n < 4000)) {
+	    n++;
+	    if (pd < (protonDeficit - deltapd))
+		pHa -= deltapH;
+	    else if (pd > (protonDeficit + deltapd))
+		pHa += deltapH;
+	    frac = Utils::CalcFrac(pHa, my_acids[AT].pK1, my_acids[AT].pK2, my_acids[AT].pK3);
+	    protonDeficit = Acid * frac;
+	    pd = ProtonDeficit(pHa);
+	}
+	//qDebug() << "  n:" << n << "pd:" << pd << "protonDeficit:" << protonDeficit << "frac:" << frac << "pHa:" << pHa;
+
+	bicarbonate = wg_bicarbonate - protonDeficit * frac / liters;
+	total_alkalinity = bicarbonate * 50 / 61;
+	ph = pHa;
+	ui->wb_phEdit->setValue(ph);
+	ui->mw_phEdit->setValue(ph);
+        product->mash_ph = ph;
+    }
+
+    if ((AT == 3) && (liters > 0)) {        // Sulfuctic / Zwavelzuur
+	RA = ui->bs_caso4Edit->value() * MMSO4 / MMCaSO4 + ui->bs_mgso4Edit->value() * MMSO4 / MMMgSO4 + Acidmg / 1000 * MMSO4 / (MMSO4 + 2);
+	RA = 1000 * RA / liters;
+	sulfate = wg_sulfate + RA;      // Not add to sulfate??
+    } else if ((AT == 1) && (liters > 0)) { // Hydrochloric, Zoutzuur
+	RA = ui->bs_cacl2Edit->value() * MMCl / MMCaCl2 + ui->bs_naclEdit->value() * MMCl / MMNaCl + Acidmg / 1000 * MMCl / (MMCl + 1);
+	RA = 1000 * RA / liters;
+	chloride = wg_chloride + RA;
+    }
+
+    double BUGU = GetBUGU();
+    ui->buguEdit->setValue(BUGU);
+    if (BUGU < 0.32)
+	ui->buguResult->setText(tr("Very malty and sweet"));
+    else if (BUGU < 0.43)
+	ui->buguResult->setText(tr("Malty, sweet"));
+    else if (BUGU < 0.52)
+	ui->buguResult->setText(tr("Balanced"));
+    else if (BUGU < 0.63)
+	ui->buguResult->setText(tr("Hoppy, bitter"));
+    else
+	ui->buguResult->setText(tr("Very hoppy, very bitter"));
+
+    double OptSO4Clratio = GetOptSO4Clratio();
+    ui->so4clEdit->setValue(OptSO4Clratio);
+    ui->cur_so4clResult->setRange(0.7 * OptSO4Clratio, 1.3 * OptSO4Clratio);
+    if (OptSO4Clratio < 0.4)
+	ui->so4clResult->setText(tr("Too malty"));
+    else if (OptSO4Clratio < 0.6)
+	ui->so4clResult->setText(tr("Very malty"));
+    else if (OptSO4Clratio < 0.8)
+	ui->so4clResult->setText(tr("Malty"));
+    else if (OptSO4Clratio < 1.5)
+	ui->so4clResult->setText(tr("Balanced"));
+    else if (OptSO4Clratio < 2.0)
+	ui->so4clResult->setText(tr("Little bitter"));
+    else if (OptSO4Clratio < 4.0)
+	ui->so4clResult->setText(tr("Bitter"));
+    else if (OptSO4Clratio < 9.0)
+	ui->so4clResult->setText(tr("Very bitter"));
+    else
+	ui->so4clResult->setText(tr("Too bitter"));
+    if (chloride > 0)
+	RA = sulfate / chloride;
+    else
+	RA = 10;
+    ui->cur_so4clEdit->setValue(RA);
+    ui->cur_so4clResult->setValue(RA);
+
+    product->wb_calcium = calcium;
+    product->wb_magnesium = magnesium;
+    product->wb_total_alkalinity = total_alkalinity;
+    product->wb_sodium = sodium;
+    product->wb_chloride = chloride;
+    product->wb_sulfate = sulfate;
+    product->wb_ph = ph;
+
+    ui->wb_caEdit->setValue(calcium);
+    ui->wb_mgEdit->setValue(magnesium);
+    ui->wb_hco3Edit->setValue(bicarbonate);
+    ui->wb_caco3Edit->setValue(total_alkalinity);
+    ui->wb_naEdit->setValue(sodium);
+    ui->wb_clEdit->setValue(chloride);
+    ui->wb_so4Edit->setValue(sulfate);
+
+    ui->wb_caEdit->setStyleSheet((calcium < 40 || calcium > 150) ? "background-color: red":"background-color: green");
+    ui->wb_mgEdit->setStyleSheet((magnesium < 5 || magnesium > 40) ? "background-color: red":"background-color: green");
+    ui->wb_naEdit->setStyleSheet((sodium > 150) ? "background-color: red":"background-color: green");
+    /*
+     * Both chloride and sulfate should be above 50 according to
+     * John Palmer. So the Cl/SO4 ratio calculation will work.
+     */
+    ui->wb_clEdit->setStyleSheet((chloride <= 50 || chloride > 150) ? "background-color: red":"background-color: green");
+    ui->wb_so4Edit->setStyleSheet((sulfate <= 50 || sulfate > 400) ? "background-color: red":"background-color: green");
+    /*
+     * (cloride + sulfate) > 500 is too high
+     */
+    if ((chloride + sulfate) > 500) {
+	ui->wb_clEdit->setStyleSheet("background-color: red");
+	ui->wb_so4Edit->setStyleSheet("background-color: red");
+    }
+    ui->wb_phEdit->setStyleSheet((ph < 5.2 || ph > 5.6) ? "background-color: red":"background-color: green");
+    ui->wb_hco3Edit->setStyleSheet((bicarbonate > 250) ? "background-color: red":"background-color: green");
+    ui->wb_caco3Edit->setStyleSheet((bicarbonate > 250) ? "background-color: red":"background-color: green");
+
+    calcSparge();
+}
+
+
+/*
+ * Based on the work of ajDeLange.
+ */
+void EditProduct::calcSparge()
+{
+    double TargetpH = product->sparge_ph;
+    double Source_pH = product->w1_ph;
+    double Source_alkalinity = product->w1_total_alkalinity;
+
+    qDebug() << "calcSparge()";
+
+    const QSignalBlocker blocker1(ui->sp_sourceEdit);
+
+    // Select watersource or fallback to the first source.
+    if (product->sparge_source == 1) { // Source 2
+	if (product->w2_ph > 0.0 && product->w2_amount > 0) {
+	    Source_pH = product->w2_ph;
+	    Source_alkalinity = product->w2_total_alkalinity;
+	} else {
+	    product->sparge_source = 0; // Source 1
+	    ui->sp_sourceEdit->setCurrentIndex(0);
+	}
+    } else if (product->sparge_source == 2) { // Mixed
+	if (product->w2_ph > 0.0 && product->w2_amount > 0) {
+	    Source_pH = product->wg_ph;
+	    Source_alkalinity = product->wg_total_alkalinity;
+	} else {
+	    product->sparge_source = 0; // Source 1
+            ui->sp_sourceEdit->setCurrentIndex(0);
+	}
+    }
+
+    // Step 1: Compute the mole fractions of carbonic (f1o), bicarbonate (f2o) and carbonate(f3o) at the water pH
+    double r1 = pow(10, Source_pH - 6.35);
+    double r2 = pow(10, Source_pH - 10.33);
+    double d = 1 + r1 + r1 * r2;
+    double f1 = 1 / d;
+    double f3 = r1 * r2 / d;
+
+    // Step 2. Compute the mole fractions at pH = 4.3 (the pH which defines alkalinity)
+    double r143 = pow(10, 4.3 - 6.35);
+    double r243 = pow(10, 4.3 - 10.33);
+    double d43 = 1 + r143 + r143 * r243;
+    double f143 = 1 / d43;
+    double f343 = r143 * r243 / d43;
+
+    // Step 4. Solve
+    //double Ct = (Source_alkalinity - 1000 * (pow(10, -4.3) - pow(10, -Source_pH))) / ((f143 - f1) + (f3 - f343));
+    double Ct = Source_alkalinity / 50 / ((f143 - f1) + (f3 - f343));
+
+    // Step 5. Compute mole fractions at desired pH
+    double r1g = pow(10, TargetpH - 6.35);
+    double r2g = pow(10, TargetpH - 10.33);
+    double dg = 1 + r1g + r1g * r2g;
+    double f1g = 1 / dg;
+    double f3g = r1g * r2g / dg;
+
+    // Step 6. Use these to compute the milliequivalents acid required per liter (mEq/L)
+    double Acid = Ct * ((f1g - f1) + (f3 - f3g)) + pow(10, -TargetpH) - pow(10, -Source_pH);  //mEq/l
+    Acid += 0.01;   // Add acid that would be required for distilled water.
+
+    //Step 8. Get the acid data.
+    int AT = product->sparge_acid_type;
+    if (AT < 0 || AT >= my_acids.size()) {
+	AT = 0;
+	product->sparge_acid_type = 0;
+	ui->sp_acidtypeEdit->setCurrentIndex(0);
+	product->sparge_acid_perc = my_acids[0].AcidPrc;
+	ui->sp_acidpercEdit->setValue(product->sparge_acid_perc);
+    }
+    double fract = Utils::CalcFrac(TargetpH, my_acids[AT].pK1, my_acids[AT].pK2, my_acids[AT].pK3);
+
+    // Step 9. Now divide the mEq required by the "fraction". This is the required number of moles of acid.
+    Acid /= fract;
+
+    // Step 10. Multiply by molecular weight of the acid
+    Acid *= my_acids[AT].MolWt; //mg
+
+    // Step 11. Divide by Specific Gravity and Percentage to get the final ml.
+    Acid = Acid / my_acids[AT].AcidSG / (product->sparge_acid_perc / 100); //ml
+    Acid *= product->sparge_volume; //ml acid total
+    Acid = round(Acid * 100.0) / 100.0;
+    product->sparge_acid_amount = Acid / 1000;
+    ui->sp_acidvolEdit->setValue(Acid);
+}
+
+
+void EditProduct::sp_source_changed(int val)
+{
+    product->sparge_source = val;
+    calcSparge();
+    is_changed();
+}
+
+
+void EditProduct::sp_type_changed(int val)
+{
+    product->sparge_acid_type = val;
+    product->sparge_acid_perc = my_acids[val].AcidPrc;
+    ui->sp_acidpercEdit->setValue(product->sparge_acid_perc);
+    calcSparge();
+    is_changed();
+}
+
+
+void EditProduct::sp_ph_changed(double val)
+{
+    product->sparge_ph = val;
+    calcSparge();
+    is_changed();
+}
+
+
+double EditProduct::GetBUGU()
+{
+    double gu = (product->est_og - 1) * 1000;
+    if (gu > 0)
+	return product->est_ibu / gu;
+    return 0.5;
+}
+
+
+double EditProduct::GetOptSO4Clratio()
+{
+    if (ui->wt_so4Edit->value() > 0 && ui->wt_clEdit->value()) {
+	/* If target water is selected .. */
+	return (ui->wt_so4Edit->value() / ui->wt_clEdit->value());
+    }
+    double BUGU = GetBUGU();
+    return (-1.2 * BUGU + 1.4);
+}
+
+
+void EditProduct::mw_calc_acid_clicked()
+{
+    product->calc_acid = ! product->calc_acid;
+    ui->mw_autoEdit->setChecked(product->calc_acid);
+    ui->mw_phEdit->setReadOnly(! product->calc_acid);
+    ui->mw_phEdit->setButtonSymbols(product->calc_acid ? QAbstractSpinBox::UpDownArrows : QAbstractSpinBox::NoButtons);
+    ui->mw_acidvolEdit->setReadOnly(product->calc_acid);
+    ui->mw_acidvolEdit->setButtonSymbols(product->calc_acid ? QAbstractSpinBox::NoButtons : QAbstractSpinBox::UpDownArrows);
+    is_changed();
+    calcWater();
+}
+
+
+void EditProduct::mw_ph_changed(double val)
+{
+    if (! product->calc_acid)
+	return;
+
+    if (product->mash_ph != val) {
+	qDebug() << "mw_ph_changed" << val << product->mash_ph;
+	product->mash_ph = val;
+	is_changed();
+	calcWater();
+    }
+}
+
+
+void EditProduct::mw_acid_changed(double val)
+{
+    if (product->calc_acid)
+	return;
+
+    qDebug() << "on_mw_acid_changed" << val;
+    QString w = my_acids[product->wa_acid_name].name_en + ' ' + my_acids[product->wa_acid_name].name_nl;
+    set_brewing_salt(w, val);
+}
+
+
+void EditProduct::mw_type_changed(int val)
+{
+    if (val == product->wa_acid_name)
+	return;
+
+    qDebug() << "on_mw_type_changed" << val << "old" << product->wa_acid_name;
+    /*
+     * First remove current acid.
+     */
+    QString w = my_acids[product->wa_acid_name].name_en + ' ' + my_acids[product->wa_acid_name].name_nl;
+    brewing_salt_sub(w, 0);
+
+    product->wa_acid_name = val;
+    w = my_acids[product->wa_acid_name].name_en + ' ' + my_acids[product->wa_acid_name].name_nl;
+
+    product->wa_acid_perc = my_acids.at(val).AcidPrc;
+    ui->mw_acidpercEdit->setValue(my_acids.at(val).AcidPrc);
+    brewing_salt_sub(w, ui->mw_acidvolEdit->value());   // For now, set old amount.
+
+    is_changed();
+    calcWater();
+}
+
+
+void EditProduct::w2_volume_changed(double val)
+{
+    qDebug() << "w2_vol_changed" << val;
+
+    if (product->w2_total_alkalinity && product->w2_sulfate) {
+	/*
+	 * Seems a valid water, but don't go over the total.
+	 */
+	if (val < (product->w1_amount + product->w2_amount)) {
+	    product->w1_amount -= val - product->w2_amount;
+	    product->w2_amount = val;
+	    ui->w1_volEdit->setValue(product->w1_amount);
+	}
+    } else {
+	/*
+	 * Invalid water, block changes.
+	 */
+	product->w2_amount = 0;
+    }
+    ui->w2_volEdit->setValue(product->w2_amount);
+
+    calcWater();
+    is_changed();
+}
+
+
+void EditProduct::w1_name_changed(int val)
+{
+    QSqlQuery query;
+
+    qDebug() << "w1_name_changed" << val;
+    const QSignalBlocker blocker1(ui->w1_nameEdit);
+    if (val == 0) {
+	/*
+	 * If no water is selected, take the default water.
+	 */
+	val = my_default_water;
+	ui->w1_nameEdit->setCurrentIndex(val);
+    }
+
+    query.prepare("SELECT * FROM inventory_waters ORDER BY record");
+    query.exec();
+    query.first();
+    for (int i = 0; i < (val - 1); i++) {
+	query.next();
+    }
+    qDebug() << "set water" << query.value(1).toString();
+
+    product->w1_name = query.value(1).toString();
+    product->w1_calcium = query.value(3).toDouble();
+    product->w1_magnesium = query.value(8).toDouble();
+    product->w1_total_alkalinity = query.value(11).toDouble();
+    product->w1_sodium = query.value(7).toDouble();
+    product->w1_chloride = query.value(6).toDouble();
+    product->w1_sulfate = query.value(5).toDouble();
+    product->w1_ph = query.value(9).toDouble();
+
+    ui->w1_caEdit->setValue(product->w1_calcium);
+    ui->w1_mgEdit->setValue(product->w1_magnesium);
+    ui->w1_hco3Edit->setValue(product->w1_total_alkalinity * 1.22);
+    ui->w1_caco3Edit->setValue(product->w1_total_alkalinity);
+    ui->w1_naEdit->setValue(product->w1_sodium);
+    ui->w1_clEdit->setValue(product->w1_chloride);
+    ui->w1_so4Edit->setValue(product->w1_sulfate);
+    ui->w1_phEdit->setValue(product->w1_ph);
+
+    is_changed();
+    calcWater();
+}
+
+
+void EditProduct::w2_name_changed(int val)
+{
+    QSqlQuery query;
+
+    qDebug() << "w2_name_changed" << val;
+
+    if (val == 0) {	// Clear water 2.
+	product->w2_name = "";
+	product->w2_calcium = 0;
+	product->w2_magnesium = 0;
+	product->w2_total_alkalinity = 0;
+	product->w2_sodium = 0;
+	product->w2_chloride = 0;
+	product->w2_sulfate = 0;
+	product->w2_ph = 0;
+	product->w1_amount += product->w2_amount;
+	product->w2_amount = 0;
+    } else {
+        query.prepare("SELECT * FROM inventory_waters ORDER BY record");
+        query.exec();
+        query.first();
+        for (int i = 0; i < (val - 1); i++) {
+            query.next();
+        }
+	qDebug() << "set water" << query.value(1).toString();
+
+	product->w2_name = query.value(1).toString();
+	product->w2_calcium = query.value(3).toDouble();
+        product->w2_magnesium = query.value(8).toDouble();
+        product->w2_total_alkalinity = query.value(11).toDouble();
+        product->w2_sodium = query.value(7).toDouble();
+        product->w2_chloride = query.value(6).toDouble();
+        product->w2_sulfate = query.value(5).toDouble();
+        product->w2_ph = query.value(9).toDouble();
+    }
+    ui->w1_volEdit->setValue(product->w1_amount);
+    ui->w2_volEdit->setValue(product->w2_amount);
+    ui->w2_caEdit->setValue(product->w2_calcium);
+    ui->w2_mgEdit->setValue(product->w2_magnesium);
+    ui->w2_hco3Edit->setValue(product->w2_total_alkalinity * 1.22);
+    ui->w2_caco3Edit->setValue(product->w2_total_alkalinity);
+    ui->w2_naEdit->setValue(product->w2_sodium);
+    ui->w2_clEdit->setValue(product->w2_chloride);
+    ui->w2_so4Edit->setValue(product->w2_sulfate);
+    ui->w2_phEdit->setValue(product->w2_ph);
+
+    is_changed();
+    calcWater();
+}
+
+
+void EditProduct::wt_target_changed(int val)
+{
+    QSqlQuery query;
+
+    if (val == 0) {
+	/* Clear values */
+	ui->wt_caEdit->setValue(0);
+	ui->wt_mgEdit->setValue(0);
+	ui->wt_hco3Edit->setValue(0);
+	ui->wt_caco3Edit->setValue(0);
+	ui->wt_naEdit->setValue(0);
+	ui->wt_clEdit->setValue(0);
+	ui->wt_so4Edit->setValue(0);
+    } else {
+	query.prepare("SELECT * FROM profile_water ORDER BY name");
+    	query.exec();
+	query.first();
+    	for (int i = 0; i < (val - 1); i++) {
+            query.next();
+    	}
+	ui->wt_caEdit->setValue(query.value(2).toDouble());
+        ui->wt_mgEdit->setValue(query.value(7).toDouble());
+        ui->wt_hco3Edit->setValue(query.value(3).toDouble());
+        ui->wt_caco3Edit->setValue(query.value(10).toDouble());
+        ui->wt_naEdit->setValue(query.value(6).toDouble());
+        ui->wt_clEdit->setValue(query.value(5).toDouble());
+        ui->wt_so4Edit->setValue(query.value(4).toDouble());
+    }
+    calcWater();
+}
+
+
+void EditProduct::adjustWaters(double factor)
+{
+    int i;
+    double amount;
+
+    if (product->mashs.size() == 0)
+	return;
+
+    double mash_infuse = 0;
+    for (i = 0; i < product->mashs.size(); i++) {
+	if (product->mashs.at(i).step_type == 0) { // Infusion
+	    amount = round(product->mashs.at(i).step_infuse_amount * factor * 1000000.0) / 1000000.0;
+	    product->mashs[i].step_infuse_amount = amount;
+   	    mash_infuse += amount;
+	    product->mashs[i].step_volume = mash_infuse;
+  	}
+    }
+
+    const QSignalBlocker blocker1(ui->w1_volEdit);
+    const QSignalBlocker blocker2(ui->w2_volEdit);
+
+    if (product->w2_amount == 0) {
+	product->w1_amount = mash_infuse;
+	ui->w1_volEdit->setValue(mash_infuse);
+    } else {
+	double w1 = (product->w1_amount / (product->w1_amount + product->w2_amount)) * mash_infuse;
+	double w2 = (product->w2_amount / (product->w1_amount + product->w2_amount)) * mash_infuse;
+	product->w1_amount = w1;
+	product->w2_amount = w2;
+	ui->w1_volEdit->setValue(product->w1_amount);
+	ui->w2_volEdit->setValue(product->w2_amount);
+    }
+    product->wg_amount = mash_infuse;
+    ui->wg_volEdit->setValue(mash_infuse);
+}
+
+
+void EditProduct::wb_cacl2_changed(double val)  { set_brewing_salt("CaCl2", val);  }
+void EditProduct::wb_caso4_changed(double val)  { set_brewing_salt("CaSO4", val);  }
+void EditProduct::wb_mgso4_changed(double val)  { set_brewing_salt("MgSO4", val);  }
+void EditProduct::wb_nacl_changed(double val)   { set_brewing_salt("NaCl", val);   }
+void EditProduct::wb_mgcl2_changed(double val)  { set_brewing_salt("MgCl2", val);  }
+void EditProduct::wb_nahco3_changed(double val) { set_brewing_salt("NaHCO3", val); }
+void EditProduct::wb_caco3_changed(double val)  { set_brewing_salt("CaCO3", val);  }
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/EditProductTab9.cpp	Thu Apr 28 22:49:13 2022 +0200
@@ -0,0 +1,20 @@
+/**
+ * EditProduct.cpp is part of bmsapp.
+ *
+ * Tab 9, brewday.
+ *
+ * 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/>.
+ */
+
+
--- a/src/PrinterDialog.cpp	Thu Apr 28 16:59:58 2022 +0200
+++ b/src/PrinterDialog.cpp	Thu Apr 28 22:49:13 2022 +0200
@@ -17,6 +17,7 @@
 #include "PrinterDialog.h"
 #include "Utils.h"
 #include "EditRecipe.h"
+#include "EditProduct.h"
 #include "config.h"
 #include "global.h"
 
--- a/src/ProdInprod.cpp	Thu Apr 28 16:59:58 2022 +0200
+++ b/src/ProdInprod.cpp	Thu Apr 28 22:49:13 2022 +0200
@@ -16,6 +16,7 @@
  */
 #include "ProdInprod.h"
 #include "MainWindow.h"
+#include "EditProduct.h"
 #include "config.h"
 #include "global.h"
 
@@ -139,11 +140,11 @@
 
 void ProdInprod::edit(int recno)
 {
-//    EditProfileMash dialog(recno, this);
+    EditProduct dialog(recno, this);
     /* Signal from editor if a refresh is needed */
-//    connect(&dialog, SIGNAL(entry_changed()), this, SLOT(refreshTable()));
-//    dialog.setModal(true);
-//    dialog.exec();
+    connect(&dialog, SIGNAL(entry_changed()), this, SLOT(refreshTable()));
+    dialog.setModal(true);
+    dialog.exec();
 }
 
 
--- a/src/global.cpp	Thu Apr 28 16:59:58 2022 +0200
+++ b/src/global.cpp	Thu Apr 28 22:49:13 2022 +0200
@@ -20,7 +20,7 @@
 QString my_yeastlab = "";
 
 Recipe *recipe;
-
+Product *product;
 
 const QStringList prod_stages({
 	QObject::tr("Plan"),
--- a/src/global.h	Thu Apr 28 16:59:58 2022 +0200
+++ b/src/global.h	Thu Apr 28 22:49:13 2022 +0200
@@ -377,7 +377,7 @@
     double	package_volume;
     double	package_infuse_amount;
     double	package_infuse_abv;
-    double	package_infuse_notes;
+    QString	package_infuse_notes;
     double	package_abv;
     double	package_ph;
     double	bottle_amount;
@@ -535,6 +535,7 @@
 
 
 extern Recipe		*recipe;
+extern Product		*product;
 
 extern QString		my_brewery_name;
 extern QByteArray	my_logoByteArray;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ui/EditProduct.ui	Thu Apr 28 22:49:13 2022 +0200
@@ -0,0 +1,4995 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>EditProduct</class>
+ <widget class="QDialog" name="EditProduct">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1152</width>
+    <height>560</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QWidget" name="overviewBox" native="true">
+     <widget class="QPushButton" name="quitButton">
+      <property name="geometry">
+       <rect>
+        <x>90</x>
+        <y>510</y>
+        <width>80</width>
+        <height>23</height>
+       </rect>
+      </property>
+      <property name="sizePolicy">
+       <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+        <horstretch>0</horstretch>
+        <verstretch>0</verstretch>
+       </sizepolicy>
+      </property>
+      <property name="text">
+       <string>Quit</string>
+      </property>
+      <property name="icon">
+       <iconset>
+        <normaloff>:icons/silk/door_out.png</normaloff>:icons/silk/door_out.png</iconset>
+      </property>
+     </widget>
+     <widget class="QPushButton" name="saveButton">
+      <property name="enabled">
+       <bool>true</bool>
+      </property>
+      <property name="geometry">
+       <rect>
+        <x>940</x>
+        <y>510</y>
+        <width>80</width>
+        <height>23</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>Save</string>
+      </property>
+      <property name="icon">
+       <iconset>
+        <normaloff>:icons/silk/disk.png</normaloff>:icons/silk/disk.png</iconset>
+      </property>
+     </widget>
+     <widget class="QPushButton" name="deleteButton">
+      <property name="enabled">
+       <bool>true</bool>
+      </property>
+      <property name="geometry">
+       <rect>
+        <x>520</x>
+        <y>510</y>
+        <width>80</width>
+        <height>23</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>Delete</string>
+      </property>
+      <property name="icon">
+       <iconset>
+        <normaloff>:icons/silk/delete.png</normaloff>:icons/silk/delete.png</iconset>
+      </property>
+     </widget>
+     <widget class="QTabWidget" name="tabWidget">
+      <property name="geometry">
+       <rect>
+        <x>0</x>
+        <y>0</y>
+        <width>1128</width>
+        <height>501</height>
+       </rect>
+      </property>
+      <property name="tabPosition">
+       <enum>QTabWidget::North</enum>
+      </property>
+      <property name="tabShape">
+       <enum>QTabWidget::Rounded</enum>
+      </property>
+      <property name="currentIndex">
+       <number>0</number>
+      </property>
+      <property name="elideMode">
+       <enum>Qt::ElideNone</enum>
+      </property>
+      <property name="usesScrollButtons">
+       <bool>true</bool>
+      </property>
+      <property name="documentMode">
+       <bool>false</bool>
+      </property>
+      <property name="tabBarAutoHide">
+       <bool>false</bool>
+      </property>
+      <widget class="QWidget" name="generic">
+       <attribute name="icon">
+        <iconset resource="../../../../../../home/mbroek/MyProjects/bmsapp/resources/icons.qrc">
+         <normaloff>:/icons/bms/beerstyles.png</normaloff>:/icons/bms/beerstyles.png</iconset>
+       </attribute>
+       <attribute name="title">
+        <string>Generic</string>
+       </attribute>
+       <widget class="QLabel" name="nameLabel">
+        <property name="geometry">
+         <rect>
+          <x>0</x>
+          <y>10</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Brew name:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QLabel" name="notesLabel">
+        <property name="geometry">
+         <rect>
+          <x>0</x>
+          <y>100</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Brew notes:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QLineEdit" name="nameEdit">
+        <property name="geometry">
+         <rect>
+          <x>140</x>
+          <y>10</y>
+          <width>591</width>
+          <height>23</height>
+         </rect>
+        </property>
+       </widget>
+       <widget class="QPlainTextEdit" name="notesEdit">
+        <property name="geometry">
+         <rect>
+          <x>140</x>
+          <y>100</y>
+          <width>881</width>
+          <height>51</height>
+         </rect>
+        </property>
+       </widget>
+       <widget class="QLabel" name="lockedLabel">
+        <property name="geometry">
+         <rect>
+          <x>740</x>
+          <y>10</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Read only:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QCheckBox" name="lockedEdit">
+        <property name="geometry">
+         <rect>
+          <x>880</x>
+          <y>10</y>
+          <width>61</width>
+          <height>21</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Yes</string>
+        </property>
+       </widget>
+       <widget class="QLabel" name="typeLabel">
+        <property name="geometry">
+         <rect>
+          <x>0</x>
+          <y>160</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Brew type:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QComboBox" name="typeEdit">
+        <property name="geometry">
+         <rect>
+          <x>140</x>
+          <y>160</y>
+          <width>181</width>
+          <height>23</height>
+         </rect>
+        </property>
+       </widget>
+       <widget class="QLabel" name="efficiencyLabel">
+        <property name="geometry">
+         <rect>
+          <x>370</x>
+          <y>160</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Efficiency:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QLabel" name="boil_timeLabel">
+        <property name="geometry">
+         <rect>
+          <x>370</x>
+          <y>190</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Boil time:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QLabel" name="batch_sizeLabel">
+        <property name="geometry">
+         <rect>
+          <x>0</x>
+          <y>190</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Batch size:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QLabel" name="boil_sizeLabel">
+        <property name="geometry">
+         <rect>
+          <x>740</x>
+          <y>190</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Boil size:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QDoubleSpinBox" name="efficiencyEdit">
+        <property name="geometry">
+         <rect>
+          <x>510</x>
+          <y>160</y>
+          <width>101</width>
+          <height>24</height>
+         </rect>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="accelerated">
+         <bool>true</bool>
+        </property>
+        <property name="suffix">
+         <string> %</string>
+        </property>
+        <property name="decimals">
+         <number>1</number>
+        </property>
+        <property name="singleStep">
+         <double>0.500000000000000</double>
+        </property>
+       </widget>
+       <widget class="QSpinBox" name="boil_timeEdit">
+        <property name="geometry">
+         <rect>
+          <x>510</x>
+          <y>190</y>
+          <width>101</width>
+          <height>24</height>
+         </rect>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="accelerated">
+         <bool>true</bool>
+        </property>
+        <property name="suffix">
+         <string> min</string>
+        </property>
+        <property name="maximum">
+         <number>1536</number>
+        </property>
+        <property name="singleStep">
+         <number>5</number>
+        </property>
+       </widget>
+       <widget class="QDoubleSpinBox" name="batch_sizeEdit">
+        <property name="geometry">
+         <rect>
+          <x>140</x>
+          <y>190</y>
+          <width>101</width>
+          <height>24</height>
+         </rect>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="accelerated">
+         <bool>true</bool>
+        </property>
+        <property name="suffix">
+         <string> L</string>
+        </property>
+        <property name="decimals">
+         <number>1</number>
+        </property>
+        <property name="maximum">
+         <double>100000.000000000000000</double>
+        </property>
+        <property name="singleStep">
+         <double>0.500000000000000</double>
+        </property>
+       </widget>
+       <widget class="QDoubleSpinBox" name="boil_sizeEdit">
+        <property name="geometry">
+         <rect>
+          <x>880</x>
+          <y>190</y>
+          <width>87</width>
+          <height>24</height>
+         </rect>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="readOnly">
+         <bool>true</bool>
+        </property>
+        <property name="buttonSymbols">
+         <enum>QAbstractSpinBox::NoButtons</enum>
+        </property>
+        <property name="accelerated">
+         <bool>true</bool>
+        </property>
+        <property name="suffix">
+         <string> L</string>
+        </property>
+        <property name="decimals">
+         <number>1</number>
+        </property>
+        <property name="maximum">
+         <double>100000.000000000000000</double>
+        </property>
+        <property name="singleStep">
+         <double>0.500000000000000</double>
+        </property>
+       </widget>
+       <widget class="QGroupBox" name="groupBox">
+        <property name="geometry">
+         <rect>
+          <x>10</x>
+          <y>340</y>
+          <width>1101</width>
+          <height>121</height>
+         </rect>
+        </property>
+        <property name="title">
+         <string>Overview</string>
+        </property>
+        <widget class="QDoubleSpinBox" name="est_ogEdit">
+         <property name="geometry">
+          <rect>
+           <x>130</x>
+           <y>20</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::UpDownArrows</enum>
+         </property>
+         <property name="accelerated">
+          <bool>true</bool>
+         </property>
+         <property name="decimals">
+          <number>3</number>
+         </property>
+         <property name="minimum">
+          <double>0.980000000000000</double>
+         </property>
+         <property name="maximum">
+          <double>2.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>0.001000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="est_abvEdit">
+         <property name="geometry">
+          <rect>
+           <x>870</x>
+           <y>20</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="accelerated">
+          <bool>false</bool>
+         </property>
+         <property name="suffix">
+          <string> %</string>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="minimum">
+          <double>0.000000000000000</double>
+         </property>
+         <property name="maximum">
+          <double>80.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>0.100000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="est_fgEdit">
+         <property name="geometry">
+          <rect>
+           <x>500</x>
+           <y>20</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="accelerated">
+          <bool>true</bool>
+         </property>
+         <property name="decimals">
+          <number>3</number>
+         </property>
+         <property name="minimum">
+          <double>0.980000000000000</double>
+         </property>
+         <property name="maximum">
+          <double>2.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>0.001000000000000</double>
+         </property>
+        </widget>
+        <widget class="QLabel" name="est_abvLabel">
+         <property name="geometry">
+          <rect>
+           <x>730</x>
+           <y>20</y>
+           <width>131</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Alcohol Volume:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="est_fgLabel">
+         <property name="geometry">
+          <rect>
+           <x>360</x>
+           <y>20</y>
+           <width>131</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Estimated FG:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="RangedSlider" name="est_fgShow">
+         <property name="geometry">
+          <rect>
+           <x>580</x>
+           <y>20</y>
+           <width>148</width>
+           <height>24</height>
+          </rect>
+         </property>
+        </widget>
+        <widget class="RangedSlider" name="est_ogShow">
+         <property name="geometry">
+          <rect>
+           <x>210</x>
+           <y>20</y>
+           <width>148</width>
+           <height>24</height>
+          </rect>
+         </property>
+        </widget>
+        <widget class="QLabel" name="est_ogLabel">
+         <property name="geometry">
+          <rect>
+           <x>5</x>
+           <y>20</y>
+           <width>116</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Estimated OG:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="RangedSlider" name="est_abvShow">
+         <property name="geometry">
+          <rect>
+           <x>950</x>
+           <y>20</y>
+           <width>148</width>
+           <height>24</height>
+          </rect>
+         </property>
+        </widget>
+        <widget class="QComboBox" name="color_methodEdit">
+         <property name="geometry">
+          <rect>
+           <x>500</x>
+           <y>50</y>
+           <width>141</width>
+           <height>23</height>
+          </rect>
+         </property>
+        </widget>
+        <widget class="RangedSlider" name="est_colorShow">
+         <property name="geometry">
+          <rect>
+           <x>210</x>
+           <y>50</y>
+           <width>148</width>
+           <height>24</height>
+          </rect>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="est_carbEdit">
+         <property name="geometry">
+          <rect>
+           <x>870</x>
+           <y>50</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="accelerated">
+          <bool>false</bool>
+         </property>
+         <property name="suffix">
+          <string> %</string>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="minimum">
+          <double>0.000000000000000</double>
+         </property>
+         <property name="maximum">
+          <double>80.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>0.100000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="est_colorEdit">
+         <property name="geometry">
+          <rect>
+           <x>130</x>
+           <y>50</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="accelerated">
+          <bool>false</bool>
+         </property>
+         <property name="decimals">
+          <number>0</number>
+         </property>
+         <property name="minimum">
+          <double>0.000000000000000</double>
+         </property>
+         <property name="maximum">
+          <double>400.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>1.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QLabel" name="color_methodLabel">
+         <property name="geometry">
+          <rect>
+           <x>360</x>
+           <y>50</y>
+           <width>131</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Color method:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="est_carbLabel">
+         <property name="geometry">
+          <rect>
+           <x>730</x>
+           <y>50</y>
+           <width>131</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Carbonation:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="RangedSlider" name="est_carbShow">
+         <property name="geometry">
+          <rect>
+           <x>950</x>
+           <y>50</y>
+           <width>148</width>
+           <height>24</height>
+          </rect>
+         </property>
+        </widget>
+        <widget class="QLabel" name="est_colorLabel">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>50</y>
+           <width>111</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Color EBC:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="RangedSlider" name="est_ibuShow">
+         <property name="geometry">
+          <rect>
+           <x>210</x>
+           <y>80</y>
+           <width>148</width>
+           <height>24</height>
+          </rect>
+         </property>
+        </widget>
+        <widget class="QComboBox" name="ibu_methodEdit">
+         <property name="geometry">
+          <rect>
+           <x>500</x>
+           <y>80</y>
+           <width>141</width>
+           <height>23</height>
+          </rect>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="est_ibuEdit">
+         <property name="geometry">
+          <rect>
+           <x>130</x>
+           <y>80</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="accelerated">
+          <bool>false</bool>
+         </property>
+         <property name="decimals">
+          <number>0</number>
+         </property>
+         <property name="minimum">
+          <double>0.000000000000000</double>
+         </property>
+         <property name="maximum">
+          <double>400.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>1.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QLabel" name="ibu_methodLabel">
+         <property name="geometry">
+          <rect>
+           <x>360</x>
+           <y>80</y>
+           <width>131</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>IBU method:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="calLabel">
+         <property name="geometry">
+          <rect>
+           <x>730</x>
+           <y>80</y>
+           <width>131</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Energy kcal/l:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="est_ibuLabel">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>80</y>
+           <width>111</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Bitterness IBU:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="calEdit">
+         <property name="geometry">
+          <rect>
+           <x>870</x>
+           <y>80</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="accelerated">
+          <bool>false</bool>
+         </property>
+         <property name="decimals">
+          <number>0</number>
+         </property>
+         <property name="minimum">
+          <double>0.000000000000000</double>
+         </property>
+         <property name="maximum">
+          <double>8000.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>0.100000000000000</double>
+         </property>
+        </widget>
+       </widget>
+       <widget class="QGroupBox" name="styleBox">
+        <property name="geometry">
+         <rect>
+          <x>10</x>
+          <y>210</y>
+          <width>1101</width>
+          <height>121</height>
+         </rect>
+        </property>
+        <property name="title">
+         <string>Beerstyle</string>
+        </property>
+        <widget class="QComboBox" name="beerstyleEdit">
+         <property name="geometry">
+          <rect>
+           <x>500</x>
+           <y>20</y>
+           <width>226</width>
+           <height>23</height>
+          </rect>
+         </property>
+        </widget>
+        <widget class="QLineEdit" name="st_groupEdit">
+         <property name="geometry">
+          <rect>
+           <x>870</x>
+           <y>50</y>
+           <width>101</width>
+           <height>23</height>
+          </rect>
+         </property>
+         <property name="dragEnabled">
+          <bool>true</bool>
+         </property>
+        </widget>
+        <widget class="QLineEdit" name="st_nameEdit">
+         <property name="geometry">
+          <rect>
+           <x>500</x>
+           <y>50</y>
+           <width>226</width>
+           <height>23</height>
+          </rect>
+         </property>
+         <property name="dragEnabled">
+          <bool>true</bool>
+         </property>
+        </widget>
+        <widget class="QLineEdit" name="st_guideEdit">
+         <property name="geometry">
+          <rect>
+           <x>130</x>
+           <y>50</y>
+           <width>226</width>
+           <height>23</height>
+          </rect>
+         </property>
+         <property name="dragEnabled">
+          <bool>true</bool>
+         </property>
+        </widget>
+        <widget class="QLabel" name="st_guideLabel">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>50</y>
+           <width>111</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Style guide:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="st_groupLabel">
+         <property name="geometry">
+          <rect>
+           <x>730</x>
+           <y>50</y>
+           <width>131</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Style group:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="st_nameLabel">
+         <property name="geometry">
+          <rect>
+           <x>360</x>
+           <y>50</y>
+           <width>131</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Style name:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="beerstyleLabel">
+         <property name="geometry">
+          <rect>
+           <x>360</x>
+           <y>20</y>
+           <width>131</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Select style:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="st_typeLabel">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>80</y>
+           <width>111</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Style type:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLineEdit" name="st_typeEdit">
+         <property name="geometry">
+          <rect>
+           <x>130</x>
+           <y>80</y>
+           <width>226</width>
+           <height>23</height>
+          </rect>
+         </property>
+         <property name="dragEnabled">
+          <bool>true</bool>
+         </property>
+        </widget>
+        <widget class="QLabel" name="st_catLabel">
+         <property name="geometry">
+          <rect>
+           <x>360</x>
+           <y>80</y>
+           <width>131</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Category:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLineEdit" name="st_catnrEdit">
+         <property name="geometry">
+          <rect>
+           <x>870</x>
+           <y>80</y>
+           <width>71</width>
+           <height>23</height>
+          </rect>
+         </property>
+         <property name="dragEnabled">
+          <bool>true</bool>
+         </property>
+        </widget>
+        <widget class="QLabel" name="st_catnrLabel">
+         <property name="geometry">
+          <rect>
+           <x>730</x>
+           <y>80</y>
+           <width>131</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Category number:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLineEdit" name="st_catEdit">
+         <property name="geometry">
+          <rect>
+           <x>500</x>
+           <y>80</y>
+           <width>226</width>
+           <height>23</height>
+          </rect>
+         </property>
+         <property name="dragEnabled">
+          <bool>true</bool>
+         </property>
+        </widget>
+       </widget>
+       <widget class="QLabel" name="codeLabel">
+        <property name="geometry">
+         <rect>
+          <x>0</x>
+          <y>40</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Brew code:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QLabel" name="splitatLabel">
+        <property name="geometry">
+         <rect>
+          <x>0</x>
+          <y>70</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Split product:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QLineEdit" name="codeEdit">
+        <property name="geometry">
+         <rect>
+          <x>140</x>
+          <y>40</y>
+          <width>181</width>
+          <height>23</height>
+         </rect>
+        </property>
+       </widget>
+       <widget class="QLabel" name="splitpartLabel">
+        <property name="geometry">
+         <rect>
+          <x>370</x>
+          <y>70</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Split part:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QLineEdit" name="splitatEdit">
+        <property name="geometry">
+         <rect>
+          <x>140</x>
+          <y>70</y>
+          <width>101</width>
+          <height>23</height>
+         </rect>
+        </property>
+       </widget>
+       <widget class="QLineEdit" name="splitpartEdit">
+        <property name="geometry">
+         <rect>
+          <x>510</x>
+          <y>70</y>
+          <width>101</width>
+          <height>23</height>
+         </rect>
+        </property>
+       </widget>
+       <widget class="QLabel" name="birthLabel">
+        <property name="geometry">
+         <rect>
+          <x>370</x>
+          <y>40</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Plan start:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QLineEdit" name="birthEdit">
+        <property name="geometry">
+         <rect>
+          <x>510</x>
+          <y>40</y>
+          <width>101</width>
+          <height>23</height>
+         </rect>
+        </property>
+       </widget>
+       <widget class="QLabel" name="stageLabel">
+        <property name="geometry">
+         <rect>
+          <x>740</x>
+          <y>40</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Plan start:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QLineEdit" name="stageEdit">
+        <property name="geometry">
+         <rect>
+          <x>880</x>
+          <y>40</y>
+          <width>101</width>
+          <height>23</height>
+         </rect>
+        </property>
+       </widget>
+       <widget class="QLabel" name="completeLabel">
+        <property name="geometry">
+         <rect>
+          <x>740</x>
+          <y>160</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Ingredients present:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+      </widget>
+      <widget class="QWidget" name="equipment">
+       <attribute name="icon">
+        <iconset resource="../../../../../../home/mbroek/MyProjects/bmsapp/resources/icons.qrc">
+         <normaloff>:/icons/bms/mash.png</normaloff>:/icons/bms/mash.png</iconset>
+       </attribute>
+       <attribute name="title">
+        <string>Equipment</string>
+       </attribute>
+      </widget>
+      <widget class="QWidget" name="fermentables">
+       <attribute name="icon">
+        <iconset resource="../../../../../../home/mbroek/MyProjects/bmsapp/resources/icons.qrc">
+         <normaloff>:/icons/bms/graan.png</normaloff>:/icons/bms/graan.png</iconset>
+       </attribute>
+       <attribute name="title">
+        <string>Fermentables</string>
+       </attribute>
+       <widget class="QLabel" name="est_color2Label">
+        <property name="geometry">
+         <rect>
+          <x>0</x>
+          <y>10</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Color EBC:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QDoubleSpinBox" name="est_color2Edit">
+        <property name="geometry">
+         <rect>
+          <x>140</x>
+          <y>10</y>
+          <width>71</width>
+          <height>24</height>
+         </rect>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="readOnly">
+         <bool>true</bool>
+        </property>
+        <property name="buttonSymbols">
+         <enum>QAbstractSpinBox::NoButtons</enum>
+        </property>
+        <property name="accelerated">
+         <bool>false</bool>
+        </property>
+        <property name="decimals">
+         <number>0</number>
+        </property>
+        <property name="minimum">
+         <double>0.000000000000000</double>
+        </property>
+        <property name="maximum">
+         <double>400.000000000000000</double>
+        </property>
+        <property name="singleStep">
+         <double>1.000000000000000</double>
+        </property>
+       </widget>
+       <widget class="QLabel" name="est_og2Label">
+        <property name="geometry">
+         <rect>
+          <x>0</x>
+          <y>40</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Estimated OG:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QDoubleSpinBox" name="est_og2Edit">
+        <property name="geometry">
+         <rect>
+          <x>140</x>
+          <y>40</y>
+          <width>71</width>
+          <height>24</height>
+         </rect>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="buttonSymbols">
+         <enum>QAbstractSpinBox::UpDownArrows</enum>
+        </property>
+        <property name="accelerated">
+         <bool>true</bool>
+        </property>
+        <property name="decimals">
+         <number>3</number>
+        </property>
+        <property name="minimum">
+         <double>0.980000000000000</double>
+        </property>
+        <property name="maximum">
+         <double>2.000000000000000</double>
+        </property>
+        <property name="singleStep">
+         <double>0.001000000000000</double>
+        </property>
+       </widget>
+       <widget class="QLabel" name="perc_mashLabel">
+        <property name="geometry">
+         <rect>
+          <x>440</x>
+          <y>10</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Mash tun %:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QLabel" name="perc_sugarLabel">
+        <property name="geometry">
+         <rect>
+          <x>440</x>
+          <y>40</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Sugars %:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QLabel" name="perc_caraLabel">
+        <property name="geometry">
+         <rect>
+          <x>440</x>
+          <y>70</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Cara/crystal %:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QLabel" name="lintnerLabel">
+        <property name="geometry">
+         <rect>
+          <x>440</x>
+          <y>100</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Lintner:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QTableWidget" name="fermentablesTable">
+        <property name="geometry">
+         <rect>
+          <x>10</x>
+          <y>130</y>
+          <width>1101</width>
+          <height>331</height>
+         </rect>
+        </property>
+       </widget>
+       <widget class="QProgressBar" name="lintnerShow">
+        <property name="geometry">
+         <rect>
+          <x>580</x>
+          <y>100</y>
+          <width>381</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="maximum">
+         <number>200</number>
+        </property>
+        <property name="value">
+         <number>24</number>
+        </property>
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="format">
+         <string>%v lintner</string>
+        </property>
+       </widget>
+       <widget class="QProgressBar" name="perc_mashShow">
+        <property name="geometry">
+         <rect>
+          <x>580</x>
+          <y>10</y>
+          <width>381</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="maximum">
+         <number>120</number>
+        </property>
+        <property name="value">
+         <number>24</number>
+        </property>
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="format">
+         <string>%v%</string>
+        </property>
+       </widget>
+       <widget class="QProgressBar" name="perc_sugarsShow">
+        <property name="geometry">
+         <rect>
+          <x>580</x>
+          <y>40</y>
+          <width>381</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="maximum">
+         <number>50</number>
+        </property>
+        <property name="value">
+         <number>24</number>
+        </property>
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="format">
+         <string>%v%</string>
+        </property>
+       </widget>
+       <widget class="QProgressBar" name="perc_caraShow">
+        <property name="geometry">
+         <rect>
+          <x>580</x>
+          <y>70</y>
+          <width>381</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="maximum">
+         <number>50</number>
+        </property>
+        <property name="value">
+         <number>24</number>
+        </property>
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="format">
+         <string>%v%</string>
+        </property>
+       </widget>
+       <widget class="QPushButton" name="addFermentable">
+        <property name="geometry">
+         <rect>
+          <x>140</x>
+          <y>100</y>
+          <width>80</width>
+          <height>23</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Add</string>
+        </property>
+        <property name="icon">
+         <iconset resource="../../../../../../home/mbroek/MyProjects/bmsapp/resources/icons.qrc">
+          <normaloff>:/icons/bms/graan.png</normaloff>:/icons/bms/graan.png</iconset>
+        </property>
+       </widget>
+      </widget>
+      <widget class="QWidget" name="hops">
+       <attribute name="icon">
+        <iconset resource="../../../../../../home/mbroek/MyProjects/bmsapp/resources/icons.qrc">
+         <normaloff>:/icons/bms/hop.png</normaloff>:/icons/bms/hop.png</iconset>
+       </attribute>
+       <attribute name="title">
+        <string>Hops</string>
+       </attribute>
+       <widget class="QLabel" name="est_ibu2Label">
+        <property name="geometry">
+         <rect>
+          <x>0</x>
+          <y>10</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Bitterness IBU:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QDoubleSpinBox" name="est_ibu2Edit">
+        <property name="geometry">
+         <rect>
+          <x>140</x>
+          <y>10</y>
+          <width>81</width>
+          <height>24</height>
+         </rect>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="readOnly">
+         <bool>true</bool>
+        </property>
+        <property name="buttonSymbols">
+         <enum>QAbstractSpinBox::NoButtons</enum>
+        </property>
+        <property name="accelerated">
+         <bool>false</bool>
+        </property>
+        <property name="decimals">
+         <number>0</number>
+        </property>
+        <property name="minimum">
+         <double>0.000000000000000</double>
+        </property>
+        <property name="maximum">
+         <double>400.000000000000000</double>
+        </property>
+        <property name="singleStep">
+         <double>1.000000000000000</double>
+        </property>
+       </widget>
+       <widget class="QLabel" name="hop_tasteLabel">
+        <property name="geometry">
+         <rect>
+          <x>220</x>
+          <y>10</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Hop taste:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QLabel" name="hop_aromaLabel">
+        <property name="geometry">
+         <rect>
+          <x>670</x>
+          <y>10</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Hop aroma:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QProgressBar" name="hop_tasteShow">
+        <property name="geometry">
+         <rect>
+          <x>360</x>
+          <y>10</y>
+          <width>301</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="maximum">
+         <number>100</number>
+        </property>
+        <property name="value">
+         <number>24</number>
+        </property>
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="format">
+         <string>%v%</string>
+        </property>
+       </widget>
+       <widget class="QProgressBar" name="hop_aromaShow">
+        <property name="geometry">
+         <rect>
+          <x>810</x>
+          <y>10</y>
+          <width>301</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="maximum">
+         <number>100</number>
+        </property>
+        <property name="value">
+         <number>24</number>
+        </property>
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="format">
+         <string>%v%</string>
+        </property>
+       </widget>
+       <widget class="QTableWidget" name="hopsTable">
+        <property name="geometry">
+         <rect>
+          <x>10</x>
+          <y>100</y>
+          <width>1101</width>
+          <height>361</height>
+         </rect>
+        </property>
+       </widget>
+       <widget class="QPushButton" name="addHop">
+        <property name="geometry">
+         <rect>
+          <x>140</x>
+          <y>70</y>
+          <width>80</width>
+          <height>23</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Add</string>
+        </property>
+        <property name="icon">
+         <iconset resource="../../../../../../home/mbroek/MyProjects/bmsapp/resources/icons.qrc">
+          <normaloff>:/icons/bms/hop.png</normaloff>:/icons/bms/hop.png</iconset>
+        </property>
+       </widget>
+      </widget>
+      <widget class="QWidget" name="miscs">
+       <attribute name="icon">
+        <iconset resource="../../../../../../home/mbroek/MyProjects/bmsapp/resources/icons.qrc">
+         <normaloff>:/icons/bms/peper.png</normaloff>:/icons/bms/peper.png</iconset>
+       </attribute>
+       <attribute name="title">
+        <string>Miscs</string>
+       </attribute>
+       <widget class="QTableWidget" name="miscsTable">
+        <property name="geometry">
+         <rect>
+          <x>140</x>
+          <y>10</y>
+          <width>849</width>
+          <height>451</height>
+         </rect>
+        </property>
+       </widget>
+       <widget class="QPushButton" name="addMisc">
+        <property name="geometry">
+         <rect>
+          <x>30</x>
+          <y>40</y>
+          <width>80</width>
+          <height>23</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Add</string>
+        </property>
+        <property name="icon">
+         <iconset resource="../../../../../../home/mbroek/MyProjects/bmsapp/resources/icons.qrc">
+          <normaloff>:/icons/bms/peper.png</normaloff>:/icons/bms/peper.png</iconset>
+        </property>
+       </widget>
+      </widget>
+      <widget class="QWidget" name="yeasts">
+       <attribute name="icon">
+        <iconset resource="../../../../../../home/mbroek/MyProjects/bmsapp/resources/icons.qrc">
+         <normaloff>:/icons/bms/erlenmeyer.png</normaloff>:/icons/bms/erlenmeyer.png</iconset>
+       </attribute>
+       <attribute name="title">
+        <string>Yeasts</string>
+       </attribute>
+       <widget class="QLabel" name="est_og3Label">
+        <property name="geometry">
+         <rect>
+          <x>50</x>
+          <y>10</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Estimated OG:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QDoubleSpinBox" name="est_og3Edit">
+        <property name="geometry">
+         <rect>
+          <x>190</x>
+          <y>10</y>
+          <width>71</width>
+          <height>24</height>
+         </rect>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="buttonSymbols">
+         <enum>QAbstractSpinBox::NoButtons</enum>
+        </property>
+        <property name="accelerated">
+         <bool>true</bool>
+        </property>
+        <property name="decimals">
+         <number>3</number>
+        </property>
+        <property name="minimum">
+         <double>0.980000000000000</double>
+        </property>
+        <property name="maximum">
+         <double>2.000000000000000</double>
+        </property>
+        <property name="singleStep">
+         <double>0.001000000000000</double>
+        </property>
+       </widget>
+       <widget class="QLabel" name="est_fg3Label">
+        <property name="geometry">
+         <rect>
+          <x>50</x>
+          <y>40</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Estimated FG:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QDoubleSpinBox" name="est_fg3Edit">
+        <property name="geometry">
+         <rect>
+          <x>190</x>
+          <y>40</y>
+          <width>71</width>
+          <height>24</height>
+         </rect>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="buttonSymbols">
+         <enum>QAbstractSpinBox::NoButtons</enum>
+        </property>
+        <property name="accelerated">
+         <bool>true</bool>
+        </property>
+        <property name="decimals">
+         <number>3</number>
+        </property>
+        <property name="minimum">
+         <double>0.980000000000000</double>
+        </property>
+        <property name="maximum">
+         <double>2.000000000000000</double>
+        </property>
+        <property name="singleStep">
+         <double>0.001000000000000</double>
+        </property>
+       </widget>
+       <widget class="QLabel" name="est_abv2Label">
+        <property name="geometry">
+         <rect>
+          <x>50</x>
+          <y>70</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Alcohol Volume:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QDoubleSpinBox" name="est_abv2Edit">
+        <property name="geometry">
+         <rect>
+          <x>190</x>
+          <y>70</y>
+          <width>71</width>
+          <height>24</height>
+         </rect>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="readOnly">
+         <bool>true</bool>
+        </property>
+        <property name="buttonSymbols">
+         <enum>QAbstractSpinBox::NoButtons</enum>
+        </property>
+        <property name="accelerated">
+         <bool>false</bool>
+        </property>
+        <property name="suffix">
+         <string> %</string>
+        </property>
+        <property name="decimals">
+         <number>1</number>
+        </property>
+        <property name="minimum">
+         <double>0.000000000000000</double>
+        </property>
+        <property name="maximum">
+         <double>80.000000000000000</double>
+        </property>
+        <property name="singleStep">
+         <double>0.100000000000000</double>
+        </property>
+       </widget>
+       <widget class="QLabel" name="est_svgLabel">
+        <property name="geometry">
+         <rect>
+          <x>30</x>
+          <y>100</y>
+          <width>151</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Appearant Attenuation:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QDoubleSpinBox" name="est_svgEdit">
+        <property name="geometry">
+         <rect>
+          <x>190</x>
+          <y>100</y>
+          <width>71</width>
+          <height>24</height>
+         </rect>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="readOnly">
+         <bool>true</bool>
+        </property>
+        <property name="buttonSymbols">
+         <enum>QAbstractSpinBox::NoButtons</enum>
+        </property>
+        <property name="accelerated">
+         <bool>false</bool>
+        </property>
+        <property name="suffix">
+         <string> %</string>
+        </property>
+        <property name="decimals">
+         <number>1</number>
+        </property>
+        <property name="minimum">
+         <double>0.000000000000000</double>
+        </property>
+        <property name="maximum">
+         <double>120.000000000000000</double>
+        </property>
+        <property name="singleStep">
+         <double>0.100000000000000</double>
+        </property>
+       </widget>
+       <widget class="QTableWidget" name="yeastsTable">
+        <property name="geometry">
+         <rect>
+          <x>10</x>
+          <y>250</y>
+          <width>1101</width>
+          <height>211</height>
+         </rect>
+        </property>
+       </widget>
+       <widget class="QStackedWidget" name="yeastProcedure">
+        <property name="geometry">
+         <rect>
+          <x>290</x>
+          <y>10</y>
+          <width>821</width>
+          <height>231</height>
+         </rect>
+        </property>
+        <property name="currentIndex">
+         <number>2</number>
+        </property>
+        <widget class="QWidget" name="yeastNot"/>
+        <widget class="QWidget" name="yeastLiquid">
+         <widget class="QLabel" name="label">
+          <property name="geometry">
+           <rect>
+            <x>200</x>
+            <y>20</y>
+            <width>341</width>
+            <height>20</height>
+           </rect>
+          </property>
+          <property name="font">
+           <font>
+            <pointsize>11</pointsize>
+            <weight>75</weight>
+            <bold>true</bold>
+           </font>
+          </property>
+          <property name="text">
+           <string>Liquid yeast advice</string>
+          </property>
+         </widget>
+         <widget class="QDoubleSpinBox" name="pitchrateEdit">
+          <property name="geometry">
+           <rect>
+            <x>200</x>
+            <y>60</y>
+            <width>91</width>
+            <height>24</height>
+           </rect>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="readOnly">
+           <bool>true</bool>
+          </property>
+          <property name="buttonSymbols">
+           <enum>QAbstractSpinBox::NoButtons</enum>
+          </property>
+          <property name="accelerated">
+           <bool>false</bool>
+          </property>
+          <property name="decimals">
+           <number>3</number>
+          </property>
+          <property name="maximum">
+           <double>1000.000000000000000</double>
+          </property>
+         </widget>
+         <widget class="QLabel" name="pitchrateLabel">
+          <property name="geometry">
+           <rect>
+            <x>10</x>
+            <y>60</y>
+            <width>181</width>
+            <height>20</height>
+           </rect>
+          </property>
+          <property name="text">
+           <string>Pitchrate million cells/ml/°P:</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+         </widget>
+         <widget class="QLabel" name="initcellsLabel">
+          <property name="geometry">
+           <rect>
+            <x>10</x>
+            <y>90</y>
+            <width>181</width>
+            <height>20</height>
+           </rect>
+          </property>
+          <property name="text">
+           <string>Initial billion cells:</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+         </widget>
+         <widget class="QLabel" name="targetcellsLabel">
+          <property name="geometry">
+           <rect>
+            <x>10</x>
+            <y>120</y>
+            <width>181</width>
+            <height>20</height>
+           </rect>
+          </property>
+          <property name="text">
+           <string>Target billion cells:</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+         </widget>
+         <widget class="QDoubleSpinBox" name="initcellsEdit">
+          <property name="geometry">
+           <rect>
+            <x>200</x>
+            <y>90</y>
+            <width>91</width>
+            <height>24</height>
+           </rect>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="readOnly">
+           <bool>true</bool>
+          </property>
+          <property name="buttonSymbols">
+           <enum>QAbstractSpinBox::NoButtons</enum>
+          </property>
+          <property name="accelerated">
+           <bool>false</bool>
+          </property>
+          <property name="decimals">
+           <number>0</number>
+          </property>
+          <property name="maximum">
+           <double>100000.000000000000000</double>
+          </property>
+         </widget>
+         <widget class="QDoubleSpinBox" name="targetcellsEdit">
+          <property name="geometry">
+           <rect>
+            <x>200</x>
+            <y>120</y>
+            <width>91</width>
+            <height>24</height>
+           </rect>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="readOnly">
+           <bool>true</bool>
+          </property>
+          <property name="buttonSymbols">
+           <enum>QAbstractSpinBox::NoButtons</enum>
+          </property>
+          <property name="accelerated">
+           <bool>false</bool>
+          </property>
+          <property name="decimals">
+           <number>0</number>
+          </property>
+          <property name="maximum">
+           <double>100000.000000000000000</double>
+          </property>
+         </widget>
+         <widget class="QLabel" name="starterLabel">
+          <property name="geometry">
+           <rect>
+            <x>10</x>
+            <y>150</y>
+            <width>181</width>
+            <height>20</height>
+           </rect>
+          </property>
+          <property name="text">
+           <string>Starter volume L:</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+         </widget>
+         <widget class="QDoubleSpinBox" name="starterEdit">
+          <property name="geometry">
+           <rect>
+            <x>200</x>
+            <y>150</y>
+            <width>91</width>
+            <height>24</height>
+           </rect>
+          </property>
+          <property name="toolTip">
+           <string>A very rough starter volume estimate.</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="readOnly">
+           <bool>true</bool>
+          </property>
+          <property name="buttonSymbols">
+           <enum>QAbstractSpinBox::NoButtons</enum>
+          </property>
+          <property name="accelerated">
+           <bool>false</bool>
+          </property>
+          <property name="decimals">
+           <number>3</number>
+          </property>
+          <property name="maximum">
+           <double>100000.000000000000000</double>
+          </property>
+         </widget>
+        </widget>
+        <widget class="QWidget" name="yeastDry">
+         <widget class="QLabel" name="label_2">
+          <property name="geometry">
+           <rect>
+            <x>170</x>
+            <y>10</y>
+            <width>261</width>
+            <height>20</height>
+           </rect>
+          </property>
+          <property name="font">
+           <font>
+            <pointsize>11</pointsize>
+            <weight>75</weight>
+            <bold>true</bold>
+           </font>
+          </property>
+          <property name="text">
+           <string>Dry yeast advice</string>
+          </property>
+         </widget>
+         <widget class="QLabel" name="lo_gr_hlLabel">
+          <property name="geometry">
+           <rect>
+            <x>40</x>
+            <y>60</y>
+            <width>121</width>
+            <height>20</height>
+           </rect>
+          </property>
+          <property name="text">
+           <string>Low grams/hl:</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+         </widget>
+         <widget class="QLabel" name="hi_gr_hlLabel">
+          <property name="geometry">
+           <rect>
+            <x>40</x>
+            <y>90</y>
+            <width>121</width>
+            <height>20</height>
+           </rect>
+          </property>
+          <property name="text">
+           <string>High grams/hl:</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+         </widget>
+         <widget class="QSpinBox" name="lo_gr_hlEdit">
+          <property name="geometry">
+           <rect>
+            <x>170</x>
+            <y>60</y>
+            <width>61</width>
+            <height>24</height>
+           </rect>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="readOnly">
+           <bool>true</bool>
+          </property>
+          <property name="buttonSymbols">
+           <enum>QAbstractSpinBox::NoButtons</enum>
+          </property>
+          <property name="maximum">
+           <number>500</number>
+          </property>
+         </widget>
+         <widget class="QSpinBox" name="hi_gr_hlEdit">
+          <property name="geometry">
+           <rect>
+            <x>170</x>
+            <y>90</y>
+            <width>61</width>
+            <height>24</height>
+           </rect>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="readOnly">
+           <bool>true</bool>
+          </property>
+          <property name="buttonSymbols">
+           <enum>QAbstractSpinBox::NoButtons</enum>
+          </property>
+          <property name="maximum">
+           <number>1000</number>
+          </property>
+         </widget>
+         <widget class="QLabel" name="lo_atLabel">
+          <property name="geometry">
+           <rect>
+            <x>240</x>
+            <y>60</y>
+            <width>31</width>
+            <height>20</height>
+           </rect>
+          </property>
+          <property name="text">
+           <string>at</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignCenter</set>
+          </property>
+         </widget>
+         <widget class="QLabel" name="hi_atLabel">
+          <property name="geometry">
+           <rect>
+            <x>240</x>
+            <y>90</y>
+            <width>31</width>
+            <height>20</height>
+           </rect>
+          </property>
+          <property name="text">
+           <string>at</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignCenter</set>
+          </property>
+         </widget>
+         <widget class="QDoubleSpinBox" name="lo_sgEdit">
+          <property name="geometry">
+           <rect>
+            <x>280</x>
+            <y>60</y>
+            <width>71</width>
+            <height>24</height>
+           </rect>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="readOnly">
+           <bool>true</bool>
+          </property>
+          <property name="buttonSymbols">
+           <enum>QAbstractSpinBox::NoButtons</enum>
+          </property>
+          <property name="accelerated">
+           <bool>false</bool>
+          </property>
+          <property name="decimals">
+           <number>3</number>
+          </property>
+          <property name="maximum">
+           <double>2.000000000000000</double>
+          </property>
+          <property name="value">
+           <double>0.000000000000000</double>
+          </property>
+         </widget>
+         <widget class="QDoubleSpinBox" name="hi_sgEdit">
+          <property name="geometry">
+           <rect>
+            <x>280</x>
+            <y>90</y>
+            <width>71</width>
+            <height>24</height>
+           </rect>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="readOnly">
+           <bool>true</bool>
+          </property>
+          <property name="buttonSymbols">
+           <enum>QAbstractSpinBox::NoButtons</enum>
+          </property>
+          <property name="accelerated">
+           <bool>false</bool>
+          </property>
+          <property name="decimals">
+           <number>3</number>
+          </property>
+          <property name="maximum">
+           <double>2.000000000000000</double>
+          </property>
+         </widget>
+         <widget class="QLabel" name="need_grLabel">
+          <property name="geometry">
+           <rect>
+            <x>40</x>
+            <y>120</y>
+            <width>121</width>
+            <height>20</height>
+           </rect>
+          </property>
+          <property name="text">
+           <string>Pitch grams:</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+         </widget>
+         <widget class="QDoubleSpinBox" name="need_grEdit">
+          <property name="geometry">
+           <rect>
+            <x>170</x>
+            <y>120</y>
+            <width>61</width>
+            <height>24</height>
+           </rect>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="readOnly">
+           <bool>true</bool>
+          </property>
+          <property name="buttonSymbols">
+           <enum>QAbstractSpinBox::NoButtons</enum>
+          </property>
+          <property name="accelerated">
+           <bool>false</bool>
+          </property>
+          <property name="decimals">
+           <number>1</number>
+          </property>
+          <property name="maximum">
+           <double>1000.000000000000000</double>
+          </property>
+         </widget>
+         <widget class="QLabel" name="pitch_grLabel">
+          <property name="geometry">
+           <rect>
+            <x>40</x>
+            <y>150</y>
+            <width>121</width>
+            <height>20</height>
+           </rect>
+          </property>
+          <property name="text">
+           <string>Pitch grams/hl:</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+         </widget>
+         <widget class="QDoubleSpinBox" name="pitch_grEdit">
+          <property name="geometry">
+           <rect>
+            <x>170</x>
+            <y>150</y>
+            <width>61</width>
+            <height>24</height>
+           </rect>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+          <property name="readOnly">
+           <bool>true</bool>
+          </property>
+          <property name="buttonSymbols">
+           <enum>QAbstractSpinBox::NoButtons</enum>
+          </property>
+          <property name="accelerated">
+           <bool>false</bool>
+          </property>
+          <property name="decimals">
+           <number>2</number>
+          </property>
+          <property name="maximum">
+           <double>1000.000000000000000</double>
+          </property>
+         </widget>
+        </widget>
+       </widget>
+       <widget class="QPushButton" name="addYeast">
+        <property name="geometry">
+         <rect>
+          <x>180</x>
+          <y>210</y>
+          <width>80</width>
+          <height>23</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Add</string>
+        </property>
+        <property name="icon">
+         <iconset resource="../../../../../../home/mbroek/MyProjects/bmsapp/resources/icons.qrc">
+          <normaloff>:/icons/bms/erlenmeyer.png</normaloff>:/icons/bms/erlenmeyer.png</iconset>
+        </property>
+       </widget>
+      </widget>
+      <widget class="QWidget" name="mash">
+       <attribute name="icon">
+        <iconset resource="../../../../../../home/mbroek/MyProjects/bmsapp/resources/icons.qrc">
+         <normaloff>:/icons/bms/mash.png</normaloff>:/icons/bms/mash.png</iconset>
+       </attribute>
+       <attribute name="title">
+        <string>Mash</string>
+       </attribute>
+       <widget class="QTableWidget" name="mashsTable">
+        <property name="geometry">
+         <rect>
+          <x>10</x>
+          <y>110</y>
+          <width>1101</width>
+          <height>351</height>
+         </rect>
+        </property>
+       </widget>
+       <widget class="QPushButton" name="addMash">
+        <property name="geometry">
+         <rect>
+          <x>140</x>
+          <y>40</y>
+          <width>80</width>
+          <height>23</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Add</string>
+        </property>
+        <property name="icon">
+         <iconset resource="../../../../../../home/mbroek/MyProjects/bmsapp/resources/icons.qrc">
+          <normaloff>:/icons/bms/mash.png</normaloff>:/icons/bms/mash.png</iconset>
+        </property>
+       </widget>
+       <widget class="QLabel" name="mash_nameLabel">
+        <property name="geometry">
+         <rect>
+          <x>0</x>
+          <y>10</y>
+          <width>131</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Mash name:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QLabel" name="mash_pickLabel">
+        <property name="geometry">
+         <rect>
+          <x>530</x>
+          <y>10</y>
+          <width>171</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Mash schedule:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QComboBox" name="mash_pickEdit">
+        <property name="geometry">
+         <rect>
+          <x>710</x>
+          <y>10</y>
+          <width>321</width>
+          <height>23</height>
+         </rect>
+        </property>
+       </widget>
+       <widget class="QLineEdit" name="mash_nameEdit">
+        <property name="geometry">
+         <rect>
+          <x>140</x>
+          <y>10</y>
+          <width>321</width>
+          <height>23</height>
+         </rect>
+        </property>
+       </widget>
+       <widget class="QLabel" name="mash_timeLabel">
+        <property name="geometry">
+         <rect>
+          <x>530</x>
+          <y>40</y>
+          <width>171</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Mash time:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QLineEdit" name="mash_timeEdit">
+        <property name="geometry">
+         <rect>
+          <x>710</x>
+          <y>40</y>
+          <width>71</width>
+          <height>23</height>
+         </rect>
+        </property>
+        <property name="readOnly">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </widget>
+      <widget class="QWidget" name="water">
+       <attribute name="icon">
+        <iconset resource="../../../../../../home/mbroek/MyProjects/bmsapp/resources/icons.qrc">
+         <normaloff>:/icons/bms/water.png</normaloff>:/icons/bms/water.png</iconset>
+       </attribute>
+       <attribute name="title">
+        <string>Water</string>
+       </attribute>
+       <widget class="QGroupBox" name="waterviewBox">
+        <property name="geometry">
+         <rect>
+          <x>170</x>
+          <y>0</y>
+          <width>941</width>
+          <height>221</height>
+         </rect>
+        </property>
+        <property name="autoFillBackground">
+         <bool>false</bool>
+        </property>
+        <property name="title">
+         <string>Water overview</string>
+        </property>
+        <property name="flat">
+         <bool>false</bool>
+        </property>
+        <property name="checkable">
+         <bool>false</bool>
+        </property>
+        <property name="checked">
+         <bool>false</bool>
+        </property>
+        <widget class="QDoubleSpinBox" name="w1_caEdit">
+         <property name="geometry">
+          <rect>
+           <x>300</x>
+           <y>50</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="w1_phEdit">
+         <property name="geometry">
+          <rect>
+           <x>860</x>
+           <y>50</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>2</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="w1_so4Edit">
+         <property name="geometry">
+          <rect>
+           <x>780</x>
+           <y>50</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="w1_clEdit">
+         <property name="geometry">
+          <rect>
+           <x>700</x>
+           <y>50</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="w1_naEdit">
+         <property name="geometry">
+          <rect>
+           <x>620</x>
+           <y>50</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="w1_caco3Edit">
+         <property name="geometry">
+          <rect>
+           <x>540</x>
+           <y>50</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="w1_hco3Edit">
+         <property name="geometry">
+          <rect>
+           <x>460</x>
+           <y>50</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="w1_mgEdit">
+         <property name="geometry">
+          <rect>
+           <x>380</x>
+           <y>50</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QLabel" name="caLabel">
+         <property name="geometry">
+          <rect>
+           <x>300</x>
+           <y>20</y>
+           <width>71</width>
+           <height>21</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Ca</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="mgLabel">
+         <property name="geometry">
+          <rect>
+           <x>380</x>
+           <y>20</y>
+           <width>71</width>
+           <height>21</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Mg</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="hco3Label">
+         <property name="geometry">
+          <rect>
+           <x>460</x>
+           <y>20</y>
+           <width>71</width>
+           <height>21</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>HCO3</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="caco3Label">
+         <property name="geometry">
+          <rect>
+           <x>540</x>
+           <y>20</y>
+           <width>71</width>
+           <height>21</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>CaCO3</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="naLabel">
+         <property name="geometry">
+          <rect>
+           <x>620</x>
+           <y>20</y>
+           <width>71</width>
+           <height>21</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Na</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="clLabel">
+         <property name="geometry">
+          <rect>
+           <x>700</x>
+           <y>20</y>
+           <width>71</width>
+           <height>21</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Cl</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="so4Label">
+         <property name="geometry">
+          <rect>
+           <x>780</x>
+           <y>20</y>
+           <width>71</width>
+           <height>21</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>SO4</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="phLabel">
+         <property name="geometry">
+          <rect>
+           <x>860</x>
+           <y>20</y>
+           <width>71</width>
+           <height>21</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>pH</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="w1_volEdit">
+         <property name="geometry">
+          <rect>
+           <x>200</x>
+           <y>50</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="accelerated">
+          <bool>false</bool>
+         </property>
+         <property name="decimals">
+          <number>2</number>
+         </property>
+         <property name="maximum">
+          <double>100000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QLabel" name="volLabel">
+         <property name="geometry">
+          <rect>
+           <x>200</x>
+           <y>20</y>
+           <width>71</width>
+           <height>21</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Volume</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="w2_hco3Edit">
+         <property name="geometry">
+          <rect>
+           <x>460</x>
+           <y>80</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="w2_phEdit">
+         <property name="geometry">
+          <rect>
+           <x>860</x>
+           <y>80</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>2</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="w2_caco3Edit">
+         <property name="geometry">
+          <rect>
+           <x>540</x>
+           <y>80</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="w2_naEdit">
+         <property name="geometry">
+          <rect>
+           <x>620</x>
+           <y>80</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="w2_so4Edit">
+         <property name="geometry">
+          <rect>
+           <x>780</x>
+           <y>80</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="w2_caEdit">
+         <property name="geometry">
+          <rect>
+           <x>300</x>
+           <y>80</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="w2_clEdit">
+         <property name="geometry">
+          <rect>
+           <x>700</x>
+           <y>80</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="w2_volEdit">
+         <property name="geometry">
+          <rect>
+           <x>200</x>
+           <y>80</y>
+           <width>85</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>false</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::UpDownArrows</enum>
+         </property>
+         <property name="accelerated">
+          <bool>true</bool>
+         </property>
+         <property name="decimals">
+          <number>2</number>
+         </property>
+         <property name="maximum">
+          <double>100000.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>0.500000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="w2_mgEdit">
+         <property name="geometry">
+          <rect>
+           <x>380</x>
+           <y>80</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wg_hco3Edit">
+         <property name="geometry">
+          <rect>
+           <x>460</x>
+           <y>110</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wg_phEdit">
+         <property name="geometry">
+          <rect>
+           <x>860</x>
+           <y>110</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>2</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wg_caco3Edit">
+         <property name="geometry">
+          <rect>
+           <x>540</x>
+           <y>110</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wg_naEdit">
+         <property name="geometry">
+          <rect>
+           <x>620</x>
+           <y>110</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wg_so4Edit">
+         <property name="geometry">
+          <rect>
+           <x>780</x>
+           <y>110</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wg_caEdit">
+         <property name="geometry">
+          <rect>
+           <x>300</x>
+           <y>110</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wg_clEdit">
+         <property name="geometry">
+          <rect>
+           <x>700</x>
+           <y>110</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wg_volEdit">
+         <property name="geometry">
+          <rect>
+           <x>200</x>
+           <y>110</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>2</number>
+         </property>
+         <property name="maximum">
+          <double>100000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wg_mgEdit">
+         <property name="geometry">
+          <rect>
+           <x>380</x>
+           <y>110</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wt_hco3Edit">
+         <property name="geometry">
+          <rect>
+           <x>460</x>
+           <y>180</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wb_phEdit">
+         <property name="geometry">
+          <rect>
+           <x>860</x>
+           <y>140</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>2</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wt_caco3Edit">
+         <property name="geometry">
+          <rect>
+           <x>540</x>
+           <y>180</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wt_naEdit">
+         <property name="geometry">
+          <rect>
+           <x>620</x>
+           <y>180</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wt_so4Edit">
+         <property name="geometry">
+          <rect>
+           <x>780</x>
+           <y>180</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wt_caEdit">
+         <property name="geometry">
+          <rect>
+           <x>300</x>
+           <y>180</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wt_clEdit">
+         <property name="geometry">
+          <rect>
+           <x>700</x>
+           <y>180</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wt_mgEdit">
+         <property name="geometry">
+          <rect>
+           <x>380</x>
+           <y>180</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wb_mgEdit">
+         <property name="geometry">
+          <rect>
+           <x>380</x>
+           <y>140</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wb_hco3Edit">
+         <property name="geometry">
+          <rect>
+           <x>460</x>
+           <y>140</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wb_so4Edit">
+         <property name="geometry">
+          <rect>
+           <x>780</x>
+           <y>140</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wb_caEdit">
+         <property name="geometry">
+          <rect>
+           <x>300</x>
+           <y>140</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wb_clEdit">
+         <property name="geometry">
+          <rect>
+           <x>700</x>
+           <y>140</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wb_caco3Edit">
+         <property name="geometry">
+          <rect>
+           <x>540</x>
+           <y>140</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="wb_naEdit">
+         <property name="geometry">
+          <rect>
+           <x>620</x>
+           <y>140</y>
+           <width>71</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QLabel" name="profileLabel">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>20</y>
+           <width>171</width>
+           <height>21</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Water profile</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QComboBox" name="w1_nameEdit">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>50</y>
+           <width>181</width>
+           <height>23</height>
+          </rect>
+         </property>
+         <property name="toolTip">
+          <string>The main brewing water</string>
+         </property>
+         <property name="currentText">
+          <string/>
+         </property>
+         <property name="placeholderText">
+          <string>Choose water</string>
+         </property>
+        </widget>
+        <widget class="QComboBox" name="w2_nameEdit">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>80</y>
+           <width>181</width>
+           <height>23</height>
+          </rect>
+         </property>
+         <property name="toolTip">
+          <string>Optional dilution water</string>
+         </property>
+         <property name="placeholderText">
+          <string>Choose dilution</string>
+         </property>
+        </widget>
+        <widget class="QComboBox" name="wt_sourceEdit">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>180</y>
+           <width>181</width>
+           <height>23</height>
+          </rect>
+         </property>
+         <property name="toolTip">
+          <string>If needed, choose a target water profile.</string>
+         </property>
+        </widget>
+        <widget class="QLabel" name="mixedLabel">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>110</y>
+           <width>171</width>
+           <height>21</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Mixed water</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="treatedLabel">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>140</y>
+           <width>171</width>
+           <height>21</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Treated water</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+       </widget>
+       <widget class="QGroupBox" name="brewsaltsBox">
+        <property name="geometry">
+         <rect>
+          <x>10</x>
+          <y>230</y>
+          <width>271</width>
+          <height>241</height>
+         </rect>
+        </property>
+        <property name="title">
+         <string>Water agents</string>
+        </property>
+        <widget class="QLabel" name="bs_cacl2Label">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>20</y>
+           <width>151</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="toolTip">
+          <string>Calcium Chloride</string>
+         </property>
+         <property name="text">
+          <string>CaCl2:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="bs_caso4Label">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>50</y>
+           <width>151</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="toolTip">
+          <string>Gypsym</string>
+         </property>
+         <property name="text">
+          <string>CaSO4:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="bs_mgso4Label">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>80</y>
+           <width>151</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="toolTip">
+          <string>Epsom</string>
+         </property>
+         <property name="text">
+          <string>MgSO4:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="bs_naclLabel">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>110</y>
+           <width>151</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="toolTip">
+          <string>Table salt</string>
+         </property>
+         <property name="text">
+          <string>NaCl:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="bs_mgcl2Label">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>140</y>
+           <width>151</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="toolTip">
+          <string>Magnesium Chloride</string>
+         </property>
+         <property name="text">
+          <string>MgCl2:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="bs_nahco3Label">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>170</y>
+           <width>151</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="toolTip">
+          <string>Baking soda</string>
+         </property>
+         <property name="text">
+          <string>NaHCO3:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="bs_caco3Label">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>200</y>
+           <width>151</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="toolTip">
+          <string>Chalk undissolved</string>
+         </property>
+         <property name="text">
+          <string>CaCO3:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="bs_cacl2Edit">
+         <property name="geometry">
+          <rect>
+           <x>170</x>
+           <y>20</y>
+           <width>91</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="toolTip">
+          <string>Calcium Chloride</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="accelerated">
+          <bool>true</bool>
+         </property>
+         <property name="suffix">
+          <string> gr</string>
+         </property>
+         <property name="decimals">
+          <number>2</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>0.100000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="bs_caso4Edit">
+         <property name="geometry">
+          <rect>
+           <x>170</x>
+           <y>50</y>
+           <width>91</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="toolTip">
+          <string>Gypsym</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="accelerated">
+          <bool>true</bool>
+         </property>
+         <property name="suffix">
+          <string> gr</string>
+         </property>
+         <property name="decimals">
+          <number>2</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>0.100000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="bs_mgso4Edit">
+         <property name="geometry">
+          <rect>
+           <x>170</x>
+           <y>80</y>
+           <width>91</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="toolTip">
+          <string>Epsom</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="accelerated">
+          <bool>true</bool>
+         </property>
+         <property name="suffix">
+          <string> gr</string>
+         </property>
+         <property name="decimals">
+          <number>2</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>0.100000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="bs_naclEdit">
+         <property name="geometry">
+          <rect>
+           <x>170</x>
+           <y>110</y>
+           <width>91</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="toolTip">
+          <string>Table salt</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="accelerated">
+          <bool>true</bool>
+         </property>
+         <property name="suffix">
+          <string> gr</string>
+         </property>
+         <property name="decimals">
+          <number>2</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>0.100000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="bs_mgcl2Edit">
+         <property name="geometry">
+          <rect>
+           <x>170</x>
+           <y>140</y>
+           <width>91</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="toolTip">
+          <string>Magnesium Chloride</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="accelerated">
+          <bool>true</bool>
+         </property>
+         <property name="suffix">
+          <string> gr</string>
+         </property>
+         <property name="decimals">
+          <number>2</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>0.100000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="bs_nahco3Edit">
+         <property name="geometry">
+          <rect>
+           <x>170</x>
+           <y>170</y>
+           <width>91</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="toolTip">
+          <string>Baking soda</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="accelerated">
+          <bool>true</bool>
+         </property>
+         <property name="suffix">
+          <string> gr</string>
+         </property>
+         <property name="decimals">
+          <number>2</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>0.100000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="bs_caco3Edit">
+         <property name="geometry">
+          <rect>
+           <x>170</x>
+           <y>200</y>
+           <width>91</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="toolTip">
+          <string>Chalk undissolved</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="accelerated">
+          <bool>true</bool>
+         </property>
+         <property name="suffix">
+          <string> gr</string>
+         </property>
+         <property name="decimals">
+          <number>2</number>
+         </property>
+         <property name="maximum">
+          <double>1000.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>0.100000000000000</double>
+         </property>
+        </widget>
+       </widget>
+       <widget class="QGroupBox" name="mashBox">
+        <property name="geometry">
+         <rect>
+          <x>350</x>
+          <y>230</y>
+          <width>421</width>
+          <height>91</height>
+         </rect>
+        </property>
+        <property name="title">
+         <string>Mash water</string>
+        </property>
+        <widget class="QLabel" name="mw_phLabel">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>20</y>
+           <width>111</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Desired pH:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="mw_autoLabel">
+         <property name="geometry">
+          <rect>
+           <x>235</x>
+           <y>20</y>
+           <width>141</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Auto calculate:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="mw_acidLabel">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>50</y>
+           <width>111</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Acid to use:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QComboBox" name="mw_acidPick">
+         <property name="geometry">
+          <rect>
+           <x>130</x>
+           <y>50</y>
+           <width>101</width>
+           <height>23</height>
+          </rect>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="mw_phEdit">
+         <property name="geometry">
+          <rect>
+           <x>130</x>
+           <y>20</y>
+           <width>101</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="accelerated">
+          <bool>true</bool>
+         </property>
+         <property name="decimals">
+          <number>2</number>
+         </property>
+         <property name="maximum">
+          <double>14.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>0.100000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="mw_acidpercEdit">
+         <property name="geometry">
+          <rect>
+           <x>240</x>
+           <y>50</y>
+           <width>61</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="accelerated">
+          <bool>false</bool>
+         </property>
+         <property name="suffix">
+          <string>%</string>
+         </property>
+         <property name="decimals">
+          <number>0</number>
+         </property>
+         <property name="maximum">
+          <double>100.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>1.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="mw_acidvolEdit">
+         <property name="geometry">
+          <rect>
+           <x>310</x>
+           <y>50</y>
+           <width>89</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="accelerated">
+          <bool>true</bool>
+         </property>
+         <property name="suffix">
+          <string> ml</string>
+         </property>
+         <property name="decimals">
+          <number>2</number>
+         </property>
+         <property name="maximum">
+          <double>10000.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>0.100000000000000</double>
+         </property>
+        </widget>
+        <widget class="QCheckBox" name="mw_autoEdit">
+         <property name="geometry">
+          <rect>
+           <x>385</x>
+           <y>20</y>
+           <width>16</width>
+           <height>21</height>
+          </rect>
+         </property>
+        </widget>
+       </widget>
+       <widget class="QGroupBox" name="spargeBox">
+        <property name="geometry">
+         <rect>
+          <x>840</x>
+          <y>230</y>
+          <width>271</width>
+          <height>241</height>
+         </rect>
+        </property>
+        <property name="title">
+         <string>Sparge water</string>
+        </property>
+        <widget class="QLabel" name="sp_volLabel">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>20</y>
+           <width>151</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Sparge water supply:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="sp_tempLabel">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>50</y>
+           <width>151</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Sparge temperature:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="sp_sourceLabel">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>80</y>
+           <width>151</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Water source:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="sp_phLabel">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>110</y>
+           <width>151</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Desired pH:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="sp_acidtypeLabel">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>140</y>
+           <width>151</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Acid type:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="sp_acidpercLabel">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>170</y>
+           <width>151</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Acid strength:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QLabel" name="sp_acidvolLabel">
+         <property name="geometry">
+          <rect>
+           <x>10</x>
+           <y>200</y>
+           <width>151</width>
+           <height>20</height>
+          </rect>
+         </property>
+         <property name="text">
+          <string>Acid needed:</string>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="sp_volEdit">
+         <property name="geometry">
+          <rect>
+           <x>170</x>
+           <y>20</y>
+           <width>91</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="accelerated">
+          <bool>true</bool>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>100000.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>0.500000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="sp_tempEdit">
+         <property name="geometry">
+          <rect>
+           <x>170</x>
+           <y>50</y>
+           <width>91</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="accelerated">
+          <bool>true</bool>
+         </property>
+         <property name="decimals">
+          <number>1</number>
+         </property>
+         <property name="maximum">
+          <double>100.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>0.500000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="sp_phEdit">
+         <property name="geometry">
+          <rect>
+           <x>170</x>
+           <y>110</y>
+           <width>91</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="accelerated">
+          <bool>true</bool>
+         </property>
+         <property name="decimals">
+          <number>2</number>
+         </property>
+         <property name="maximum">
+          <double>14.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>0.100000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="sp_acidpercEdit">
+         <property name="geometry">
+          <rect>
+           <x>170</x>
+           <y>170</y>
+           <width>91</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="accelerated">
+          <bool>false</bool>
+         </property>
+         <property name="suffix">
+          <string>%</string>
+         </property>
+         <property name="decimals">
+          <number>0</number>
+         </property>
+         <property name="maximum">
+          <double>100.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>1.000000000000000</double>
+         </property>
+        </widget>
+        <widget class="QDoubleSpinBox" name="sp_acidvolEdit">
+         <property name="geometry">
+          <rect>
+           <x>170</x>
+           <y>200</y>
+           <width>91</width>
+           <height>24</height>
+          </rect>
+         </property>
+         <property name="alignment">
+          <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+         </property>
+         <property name="readOnly">
+          <bool>true</bool>
+         </property>
+         <property name="buttonSymbols">
+          <enum>QAbstractSpinBox::NoButtons</enum>
+         </property>
+         <property name="accelerated">
+          <bool>false</bool>
+         </property>
+         <property name="suffix">
+          <string> ml</string>
+         </property>
+         <property name="decimals">
+          <number>2</number>
+         </property>
+         <property name="maximum">
+          <double>10000.000000000000000</double>
+         </property>
+         <property name="singleStep">
+          <double>0.100000000000000</double>
+         </property>
+        </widget>
+        <widget class="QComboBox" name="sp_sourceEdit">
+         <property name="geometry">
+          <rect>
+           <x>170</x>
+           <y>80</y>
+           <width>91</width>
+           <height>23</height>
+          </rect>
+         </property>
+        </widget>
+        <widget class="QComboBox" name="sp_acidtypeEdit">
+         <property name="geometry">
+          <rect>
+           <x>170</x>
+           <y>140</y>
+           <width>91</width>
+           <height>23</height>
+          </rect>
+         </property>
+        </widget>
+       </widget>
+       <widget class="QLabel" name="buguLabel">
+        <property name="geometry">
+         <rect>
+          <x>290</x>
+          <y>340</y>
+          <width>181</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Bitterness index:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QDoubleSpinBox" name="buguEdit">
+        <property name="geometry">
+         <rect>
+          <x>480</x>
+          <y>340</y>
+          <width>71</width>
+          <height>24</height>
+         </rect>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="readOnly">
+         <bool>true</bool>
+        </property>
+        <property name="buttonSymbols">
+         <enum>QAbstractSpinBox::NoButtons</enum>
+        </property>
+        <property name="accelerated">
+         <bool>false</bool>
+        </property>
+        <property name="decimals">
+         <number>2</number>
+        </property>
+        <property name="maximum">
+         <double>1000.000000000000000</double>
+        </property>
+        <property name="singleStep">
+         <double>0.100000000000000</double>
+        </property>
+       </widget>
+       <widget class="QLabel" name="buguResult">
+        <property name="geometry">
+         <rect>
+          <x>560</x>
+          <y>340</y>
+          <width>211</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="font">
+         <font>
+          <italic>true</italic>
+         </font>
+        </property>
+        <property name="text">
+         <string>N/A</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QLabel" name="so4clLabel">
+        <property name="geometry">
+         <rect>
+          <x>290</x>
+          <y>370</y>
+          <width>181</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Preffered SO4:Cl ratio:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QDoubleSpinBox" name="so4clEdit">
+        <property name="geometry">
+         <rect>
+          <x>480</x>
+          <y>370</y>
+          <width>71</width>
+          <height>24</height>
+         </rect>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="readOnly">
+         <bool>true</bool>
+        </property>
+        <property name="buttonSymbols">
+         <enum>QAbstractSpinBox::NoButtons</enum>
+        </property>
+        <property name="accelerated">
+         <bool>false</bool>
+        </property>
+        <property name="decimals">
+         <number>1</number>
+        </property>
+        <property name="maximum">
+         <double>1000.000000000000000</double>
+        </property>
+        <property name="singleStep">
+         <double>0.100000000000000</double>
+        </property>
+       </widget>
+       <widget class="QLabel" name="so4clResult">
+        <property name="geometry">
+         <rect>
+          <x>560</x>
+          <y>370</y>
+          <width>211</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="font">
+         <font>
+          <italic>true</italic>
+         </font>
+        </property>
+        <property name="text">
+         <string>N/A</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QLabel" name="cur_so4clLabel">
+        <property name="geometry">
+         <rect>
+          <x>290</x>
+          <y>400</y>
+          <width>181</width>
+          <height>20</height>
+         </rect>
+        </property>
+        <property name="text">
+         <string>Current SO4:Cl ratio:</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+       </widget>
+       <widget class="QDoubleSpinBox" name="cur_so4clEdit">
+        <property name="geometry">
+         <rect>
+          <x>480</x>
+          <y>400</y>
+          <width>71</width>
+          <height>24</height>
+         </rect>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        </property>
+        <property name="readOnly">
+         <bool>true</bool>
+        </property>
+        <property name="buttonSymbols">
+         <enum>QAbstractSpinBox::NoButtons</enum>
+        </property>
+        <property name="accelerated">
+         <bool>false</bool>
+        </property>
+        <property name="decimals">
+         <number>1</number>
+        </property>
+        <property name="maximum">
+         <double>1000.000000000000000</double>
+        </property>
+        <property name="singleStep">
+         <double>0.100000000000000</double>
+        </property>
+       </widget>
+       <widget class="RangedSlider" name="cur_so4clResult">
+        <property name="geometry">
+         <rect>
+          <x>559</x>
+          <y>400</y>
+          <width>211</width>
+          <height>20</height>
+         </rect>
+        </property>
+       </widget>
+      </widget>
+      <widget class="QWidget" name="brewday">
+       <attribute name="title">
+        <string>Brewday</string>
+       </attribute>
+      </widget>
+      <widget class="QWidget" name="ferment">
+       <attribute name="title">
+        <string>Fermenting</string>
+       </attribute>
+      </widget>
+      <widget class="QWidget" name="package">
+       <attribute name="title">
+        <string>Package</string>
+       </attribute>
+      </widget>
+      <widget class="QWidget" name="taste">
+       <attribute name="title">
+        <string>Tasting</string>
+       </attribute>
+      </widget>
+     </widget>
+     <widget class="QPushButton" name="exportButton">
+      <property name="geometry">
+       <rect>
+        <x>300</x>
+        <y>510</y>
+        <width>80</width>
+        <height>23</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>Export</string>
+      </property>
+      <property name="icon">
+       <iconset resource="../../../../../../home/mbroek/MyProjects/bmsapp/resources/icons.qrc">
+        <normaloff>:/icons/silk/disk_multiple.png</normaloff>:/icons/silk/disk_multiple.png</iconset>
+      </property>
+     </widget>
+     <widget class="QPushButton" name="printButton">
+      <property name="geometry">
+       <rect>
+        <x>730</x>
+        <y>510</y>
+        <width>80</width>
+        <height>23</height>
+       </rect>
+      </property>
+      <property name="text">
+       <string>Print</string>
+      </property>
+      <property name="icon">
+       <iconset resource="../../../../../../home/mbroek/MyProjects/bmsapp/resources/icons.qrc">
+        <normaloff>:/icons/silk/printer.png</normaloff>:/icons/silk/printer.png</iconset>
+      </property>
+     </widget>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>RangedSlider</class>
+   <extends>QWidget</extends>
+   <header>RangedSlider.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>quitButton</tabstop>
+  <tabstop>deleteButton</tabstop>
+  <tabstop>saveButton</tabstop>
+ </tabstops>
+ <resources>
+  <include location="../../../../../../home/mbroek/MyProjects/bmsapp/resources/icons.qrc"/>
+ </resources>
+ <connections/>
+</ui>

mercurial