diff --git a/images/iconMash.svg b/images/iconMash.svg
new file mode 100644
index 00000000..dfa126ce
--- /dev/null
+++ b/images/iconMash.svg
@@ -0,0 +1,12 @@
+
diff --git a/resources.qrc b/resources.qrc
index c60311a5..5bd80a6c 100644
--- a/resources.qrc
+++ b/resources.qrc
@@ -82,6 +82,7 @@
images/grain2glass.svg
images/help-contents.png
images/hydrometer.svg
+ images/iconMash.svg
images/kbruch.png
images/mashpaddle.svg
images/merge.png
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index e97d60d8..e7d8ec32 100644
--- a/src/MainWindow.cpp
+++ b/src/MainWindow.cpp
@@ -1671,7 +1671,7 @@ void MainWindow::treeActivated(const QModelIndex &index) {
{
Water * w = active->getItem(index);
if (w) {
- this->pimpl->m_waterEditor->setWater(ObjectStoreWrapper::getSharedFromRaw(w));
+ this->pimpl->m_waterEditor->setEditItem(ObjectStoreWrapper::getSharedFromRaw(w));
this->pimpl->m_waterEditor->show();
}
}
@@ -2506,15 +2506,11 @@ void MainWindow::editYeastOfSelectedYeastAddition() {
return;
}
-void MainWindow::newRecipe()
-{
- QString name = QInputDialog::getText(this, tr("Recipe name"),
- tr("Recipe name:"));
- QVariant defEquipKey = PersistentSettings::value(PersistentSettings::Names::defaultEquipmentKey, -1);
- QObject* selection = sender();
-
- if( name.isEmpty() )
+void MainWindow::newRecipe() {
+ QString const name = QInputDialog::getText(this, tr("Recipe name"), tr("Recipe name:"));
+ if (name.isEmpty()) {
return;
+ }
std::shared_ptr newRec = std::make_shared(name);
@@ -2526,9 +2522,22 @@ void MainWindow::newRecipe()
return;
}
ObjectStoreWrapper::insert(newRec);
- std::shared_ptr newBoil = std::make_shared(QString("Boil for").arg(name));
+
+ //
+ // .:TODO:. For the moment, we still assume that every Recipe has a Boil and a Fermentation. Also, for the moment,
+ // we also create a new boil and fermentation for every Recipe. In time, I'd like to extend the UI so that, when you
+ // input the name for your new Recipe, you also select Mash, Boil, Fermentation (all either from "List of existing"
+ // or "Make new").
+ //
+ std::shared_ptr newBoil = std::make_shared(tr("Automatically-created Boil for %1").arg(name));
+ // NB: Recipe::setBoil will ensure Boil is stored in the database
newRec->setBoil(newBoil);
- ObjectStoreWrapper::insert(newBoil);
+ // Since we're auto-creating a Boil, it might as well start out with the "standard" profile
+ newBoil->ensureStandardProfile();
+
+ std::shared_ptr newFermentation = std::make_shared(tr("Automatically-created Fermentation for %1").arg(name));
+ // NB: Recipe::setFermentation will ensure Fermentation is stored in the database
+ newRec->setFermentation(newFermentation);
// Set the following stuff so everything appears nice
// and the calculations don't divide by zero... things like that.
@@ -2536,7 +2545,8 @@ void MainWindow::newRecipe()
newBoil->setPreBoilSize_l(23.47); // 6.2 gallons
newRec->setEfficiency_pct(70.0);
- // we need a valid key, so insert the recipe before we add equipment
+ // We need a valid key, so insert the recipe before we add equipment
+ QVariant const defEquipKey = PersistentSettings::value(PersistentSettings::Names::defaultEquipmentKey, -1);
if (defEquipKey != -1) {
auto equipment = ObjectStoreWrapper::getById(defEquipKey.toInt());
// I really want to do this before we've written the object to the
@@ -2549,8 +2559,9 @@ void MainWindow::newRecipe()
}
}
- // a new recipe will be put in a folder if you right click on a recipe or
+ // A new recipe will be put in a folder if you right click on a recipe or
// folder. Otherwise, it goes into the main window?
+ QObject* selection = this->sender();
if (selection) {
TreeView* sent = qobject_cast(tabWidget_Trees->currentWidget()->focusWidget());
if (sent) {
diff --git a/src/WaterDialog.cpp b/src/WaterDialog.cpp
index 270d2ca0..4cfabdee 100644
--- a/src/WaterDialog.cpp
+++ b/src/WaterDialog.cpp
@@ -294,12 +294,12 @@ void WaterDialog::setRecipe(Recipe *rec) {
spinBox_spargeRO->setValue( QVariant(m_spargeRO * 100).toInt());
baseProfileButton->setWater(this->m_base);
- m_base_editor->setWater(this->m_base);
+ m_base_editor->setEditItem(this->m_base);
// all of the magic to set the sliders happens in newTotals(). So don't do it twice
}
if (this->m_target && this->m_target != this->m_base) {
targetProfileButton->setWater(this->m_target);
- m_target_editor->setWater(this->m_target);
+ m_target_editor->setEditItem(this->m_target);
this->setDigits();
}
@@ -327,7 +327,7 @@ void WaterDialog::update_baseProfile(int selected) {
qDebug() << Q_FUNC_INFO << "Made base child" << *this->m_base << "from parent" << parent;
baseProfileButton->setWater(this->m_base);
- m_base_editor->setWater(this->m_base);
+ m_base_editor->setEditItem(this->m_base);
newTotals();
}
return;
@@ -351,7 +351,7 @@ void WaterDialog::update_targetProfile(int selected) {
qDebug() << Q_FUNC_INFO << "Made target child" << *this->m_target << "from parent" << parent;
targetProfileButton->setWater(this->m_target);
- m_target_editor->setWater(this->m_target);
+ m_target_editor->setEditItem(this->m_target);
this->setDigits();
}
diff --git a/src/database/DatabaseSchemaHelper.cpp b/src/database/DatabaseSchemaHelper.cpp
index a1390926..e13edcbf 100644
--- a/src/database/DatabaseSchemaHelper.cpp
+++ b/src/database/DatabaseSchemaHelper.cpp
@@ -844,7 +844,7 @@ namespace {
{QString("ALTER TABLE water ADD COLUMN iron_ppm %1").arg(db.getDbNativeTypeName())},
{QString("ALTER TABLE water ADD COLUMN nitrate_ppm %1").arg(db.getDbNativeTypeName())},
{QString("ALTER TABLE water ADD COLUMN nitrite_ppm %1").arg(db.getDbNativeTypeName())},
- {QString("ALTER TABLE water ADD COLUMN flouride_ppm %1").arg(db.getDbNativeTypeName())},
+ {QString("ALTER TABLE water ADD COLUMN flouride_ppm %1").arg(db.getDbNativeTypeName())}, // Should have been fluoride_ppm!
//
// Equipment: Extended and additional fields for BeerJSON. This includes changing a lot of column names as
// BeerJSON essentially has a record per vessel ("HLT", "Mash Tun", etc)
@@ -2188,6 +2188,7 @@ namespace {
case 12:
ret &= migrate_to_13(database, sqlQuery);
break;
+ // TODO: On next DB update, correct water.flouride_ppm to water.fluoride_ppm
default:
qCritical() << QString("Unknown version %1").arg(oldVersion);
return false;
@@ -2302,7 +2303,7 @@ bool DatabaseSchemaHelper::migrate(Database & database, int oldVersion, int newV
// By the magic of RAII, this will abort if we exit this function (including by throwing an exception) without
// having called dbTransaction.commit(). (It will also turn foreign keys back on either way -- whether the
// transaction is committed or rolled back.)
- DbTransaction dbTransaction{database, connection, DbTransaction::DISABLE_FOREIGN_KEYS};
+ DbTransaction dbTransaction{database, connection, "Migrate", DbTransaction::DISABLE_FOREIGN_KEYS};
for ( ; oldVersion < newVersion && ret; ++oldVersion ) {
ret &= migrateNext(database, oldVersion, connection);
diff --git a/src/database/DbTransaction.cpp b/src/database/DbTransaction.cpp
index 11eb3391..9e50c25b 100644
--- a/src/database/DbTransaction.cpp
+++ b/src/database/DbTransaction.cpp
@@ -1,5 +1,5 @@
/*======================================================================================================================
- * database/DbTransaction.cpp is part of Brewken, and is copyright the following authors 2021-2022:
+ * database/DbTransaction.cpp is part of Brewken, and is copyright the following authors 2021-2024:
* • Matt Young
*
* Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
@@ -19,11 +19,15 @@
#include
#include "database/Database.h"
+#include "Logging.h"
-
-DbTransaction::DbTransaction(Database & database, QSqlDatabase & connection, DbTransaction::SpecialBehaviours specialBehaviours) :
+DbTransaction::DbTransaction(Database & database,
+ QSqlDatabase & connection,
+ QString const nameForLogging,
+ DbTransaction::SpecialBehaviours specialBehaviours) :
database{database},
connection{connection},
+ nameForLogging{nameForLogging},
committed{false},
specialBehaviours{specialBehaviours} {
// Note that, on SQLite at least, turning foreign keys on and off has to happen outside a transaction, so we have to
@@ -33,9 +37,12 @@ DbTransaction::DbTransaction(Database & database, QSqlDatabase & connection, DbT
}
bool succeeded = this->connection.transaction();
- qDebug() << Q_FUNC_INFO << "Database transaction begin: " << (succeeded ? "succeeded" : "failed");
+ qDebug() <<
+ Q_FUNC_INFO << "Database transaction" << this->nameForLogging << "begin: " << (succeeded ? "succeeded" : "failed");
if (!succeeded) {
- qCritical() << Q_FUNC_INFO << "Unable to start database transaction:" << connection.lastError().text();
+ qCritical() <<
+ Q_FUNC_INFO << "Unable to start database transaction" << this->nameForLogging << ":" << connection.lastError().text();
+ qCritical().noquote() << Q_FUNC_INFO << Logging::getStackTrace();
}
return;
}
@@ -44,9 +51,11 @@ DbTransaction::~DbTransaction() {
qDebug() << Q_FUNC_INFO;
if (!committed) {
bool succeeded = this->connection.rollback();
- qDebug() << Q_FUNC_INFO << "Database transaction rollback: " << (succeeded ? "succeeded" : "failed");
+ qDebug() <<
+ Q_FUNC_INFO << "Database transaction" << this->nameForLogging << "rollback: " << (succeeded ? "succeeded" : "failed");
if (!succeeded) {
- qCritical() << Q_FUNC_INFO << "Unable to rollback database transaction:" << connection.lastError().text();
+ qCritical() <<
+ Q_FUNC_INFO << "Unable to rollback database transaction" << this->nameForLogging << ":" << connection.lastError().text();
}
}
@@ -59,9 +68,11 @@ DbTransaction::~DbTransaction() {
bool DbTransaction::commit() {
this->committed = connection.commit();
- qDebug() << Q_FUNC_INFO << "Database transaction commit: " << (this->committed ? "succeeded" : "failed");
+ qDebug() <<
+ Q_FUNC_INFO << "Database transaction" << this->nameForLogging << "commit: " << (this->committed ? "succeeded" : "failed");
if (!this->committed) {
- qCritical() << Q_FUNC_INFO << "Unable to commit database transaction:" << connection.lastError().text();
+ qCritical() <<
+ Q_FUNC_INFO << "Unable to commit database transaction" << this->nameForLogging << ":" << connection.lastError().text();
}
return this->committed;
}
diff --git a/src/database/DbTransaction.h b/src/database/DbTransaction.h
index 4d00546c..537f4ad1 100644
--- a/src/database/DbTransaction.h
+++ b/src/database/DbTransaction.h
@@ -1,5 +1,5 @@
/*======================================================================================================================
- * database/DbTransaction.h is part of Brewken, and is copyright the following authors 2021:
+ * database/DbTransaction.h is part of Brewken, and is copyright the following authors 2021-2024:
* • Matt Young
*
* Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License
@@ -34,7 +34,10 @@ class DbTransaction {
/**
* \brief Constructing a \c DbTransaction will start a DB transaction
*/
- DbTransaction(Database & database, QSqlDatabase & connection, SpecialBehaviours specialBehaviours = NONE);
+ DbTransaction(Database & database,
+ QSqlDatabase & connection,
+ QString const nameForLogging = "???",
+ SpecialBehaviours specialBehaviours = NONE);
/**
* \brief When a \c DbTransaction goes out of scope and its destructor is called, the transaction started in the
@@ -53,6 +56,9 @@ class DbTransaction {
Database & database;
// This is intended to be a short-lived object, so it's OK to store a reference to a QSqlDatabase object
QSqlDatabase & connection;
+ // This is useful for diagnosing problems such as
+ // 'Unable to start database transaction: "cannot start a transaction within a transaction Unable to begin transaction"'
+ QString const nameForLogging;
bool committed;
int specialBehaviours;
diff --git a/src/database/DefaultContentLoader.cpp b/src/database/DefaultContentLoader.cpp
index c95569a3..240eed94 100644
--- a/src/database/DefaultContentLoader.cpp
+++ b/src/database/DefaultContentLoader.cpp
@@ -156,6 +156,8 @@ DefaultContentLoader::UpdateResult DefaultContentLoader::updateContentIfNecessar
allRecipesBeforeImport.begin(), allRecipesBeforeImport.end(),
std::back_inserter(newlyImportedRecipes));
qDebug() << Q_FUNC_INFO << newlyImportedRecipes.size() << "newly imported Recipes";
+ // TODO: It would be neat, at some point, to to have a mechanism for setting a property on multiple objects
+ // of the same type, so that we could do it in a single DB update.
for (auto recipe : newlyImportedRecipes) {
recipe->setFolder(FOLDER_FOR_SUPPLIED_RECIPES);
}
diff --git a/src/database/ObjectStore.cpp b/src/database/ObjectStore.cpp
index 5cbc2871..208903cb 100644
--- a/src/database/ObjectStore.cpp
+++ b/src/database/ObjectStore.cpp
@@ -1409,7 +1409,9 @@ void ObjectStore::loadAll(Database * database) {
//
// .:TBD:. In theory we don't need a transaction if we're _only_ reading data...
QSqlDatabase connection = this->pimpl->database->sqlDatabase();
- DbTransaction dbTransaction{*this->pimpl->database, connection};
+ DbTransaction dbTransaction{*this->pimpl->database,
+ connection,
+ QString("Load All %1").arg(*this->pimpl->primaryTable.tableName)};
//
// Using QSqlTableModel would save us having to write a SELECT statement, however it is a bit hard to use it to
@@ -1710,7 +1712,9 @@ int ObjectStore::insert(std::shared_ptr object) {
// Start transaction
// (By the magic of RAII, this will abort if we return from this function without calling dbTransaction.commit()
QSqlDatabase connection = this->pimpl->database->sqlDatabase();
- DbTransaction dbTransaction{*this->pimpl->database, connection};
+ DbTransaction dbTransaction{*this->pimpl->database,
+ connection,
+ QString("Insert %1").arg(*this->pimpl->primaryTable.tableName)};
int primaryKey = this->pimpl->insertObjectInDb(connection, *object, false);
@@ -1749,7 +1753,9 @@ void ObjectStore::update(std::shared_ptr object) {
// Start transaction
// (By the magic of RAII, this will abort if we return from this function without calling dbTransaction.commit()
QSqlDatabase connection = this->pimpl->database->sqlDatabase();
- DbTransaction dbTransaction{*this->pimpl->database, connection};
+ DbTransaction dbTransaction{*this->pimpl->database,
+ connection,
+ QString("Update %1").arg(*this->pimpl->primaryTable.tableName)};
//
// Construct the SQL, which will be of the form
@@ -1875,7 +1881,11 @@ void ObjectStore::updateProperty(QObject const & object, BtStringConst const & p
// Start transaction
// (By the magic of RAII, this will abort if we return from this function without calling dbTransaction.commit()
QSqlDatabase connection = this->pimpl->database->sqlDatabase();
- DbTransaction dbTransaction{*this->pimpl->database, connection};
+ DbTransaction dbTransaction{
+ *this->pimpl->database,
+ connection,
+ QString("Update property %1 on %2").arg(*propertyName).arg(*this->pimpl->primaryTable.tableName)
+ };
if (!this->pimpl->updatePropertyInDb(connection, object, propertyName)) {
// Something went wrong. Bailing out here will abort the transaction and avoid sending the signal.
@@ -1918,7 +1928,9 @@ std::shared_ptr ObjectStore::defaultHardDelete(int id) {
qDebug() << Q_FUNC_INFO << "Hard delete" << this->pimpl->m_className << "#" << id;
auto object = this->pimpl->allObjects.value(id);
QSqlDatabase connection = this->pimpl->database->sqlDatabase();
- DbTransaction dbTransaction{*this->pimpl->database, connection};
+ DbTransaction dbTransaction{*this->pimpl->database,
+ connection,
+ QString("Hard delete %1").arg(*this->pimpl->primaryTable.tableName)};
// We'll use this in a couple of places below
QVariant primaryKey{id};
diff --git a/src/database/ObjectStoreTyped.cpp b/src/database/ObjectStoreTyped.cpp
index 6a44f538..354291b0 100644
--- a/src/database/ObjectStoreTyped.cpp
+++ b/src/database/ObjectStoreTyped.cpp
@@ -582,7 +582,8 @@ namespace {
{ObjectStore::FieldType::Double, "iron_ppm" , PropertyNames::Water::iron_ppm },
{ObjectStore::FieldType::Double, "nitrate_ppm" , PropertyNames::Water::nitrate_ppm },
{ObjectStore::FieldType::Double, "nitrite_ppm" , PropertyNames::Water::nitrite_ppm },
- {ObjectStore::FieldType::Double, "flouride_ppm" , PropertyNames::Water::flouride_ppm },
+ // .:TODO:. We should correct the typo in this column name (copy-and-paste from BeerJSON
+ {ObjectStore::FieldType::Double, "flouride_ppm" , PropertyNames::Water::fluoride_ppm },
}
};
@@ -1041,7 +1042,7 @@ bool WriteAllObjectStoresToNewDb(Database & newDatabase, QSqlDatabase & connecti
// having called dbTransaction.commit(). (It will also turn foreign keys back on either way -- whether the
// transaction is committed or rolled back.)
//
- DbTransaction dbTransaction{newDatabase, connectionNew, DbTransaction::DISABLE_FOREIGN_KEYS};
+ DbTransaction dbTransaction{newDatabase, connectionNew, "Write All", DbTransaction::DISABLE_FOREIGN_KEYS};
for (ObjectStore const * objectStore : getAllObjectStores()) {
if (!objectStore->writeAllToNewDb(newDatabase, connectionNew)) {
diff --git a/src/editors/BoilEditor.cpp b/src/editors/BoilEditor.cpp
index c9cd0a48..1e444bfc 100644
--- a/src/editors/BoilEditor.cpp
+++ b/src/editors/BoilEditor.cpp
@@ -22,44 +22,23 @@
#include "model/Boil.h"
#include "model/Recipe.h"
-BoilEditor::BoilEditor(QWidget* parent) :
+BoilEditor::BoilEditor(QWidget* parent, QString const editorName) :
QDialog(parent),
- EditorWithRecipeBase() {
+ EditorBase(editorName) {
this->setupUi(this);
this->postSetupUiInit(
{
- EDITOR_FIELD(Boil, label_name , lineEdit_name , PropertyNames::NamedEntity::name ),
- EDITOR_FIELD(Boil, label_description, textEdit_description, PropertyNames::Boil::description ),
- EDITOR_FIELD(Boil, label_preBoilSize, lineEdit_preBoilSize, PropertyNames::Boil::preBoilSize_l, 2),
- EDITOR_FIELD(Boil, label_notes , textEdit_notes , PropertyNames::Boil::notes )
+ EDITOR_FIELD_NORM(Boil, label_name , lineEdit_name , NamedEntity::name ),
+ EDITOR_FIELD_NORM(Boil, label_description, textEdit_description, Boil::description ),
+ EDITOR_FIELD_NORM(Boil, label_preBoilSize, lineEdit_preBoilSize, Boil::preBoilSize_l, 2),
+ EDITOR_FIELD_NORM(Boil, label_notes , textEdit_notes , Boil::notes ),
}
);
- // NB: label_description / textEdit_description don't need initialisation here as neither is a smart field
- // NB: label_notes / textEdit_notes don't need initialisation here as neither is a smart field
-/// SMART_FIELD_INIT(BoilEditor, label_name , lineEdit_name , Boil, PropertyNames::NamedEntity::name );
-/// SMART_FIELD_INIT(BoilEditor, label_preBoilSize, lineEdit_preBoilSize, Boil, PropertyNames::Boil::preBoilSize_l, 2);
-
-/// connect(this, &QDialog::accepted, this, &BoilEditor::saveAndClose);
-/// connect(this, &QDialog::rejected, this, &BoilEditor::closeEditor );
-
-/// this->connectSignalsAndSlots();
return;
}
BoilEditor::~BoilEditor() = default;
-void BoilEditor::writeFieldsToEditItem() {
- return;
-}
-
-void BoilEditor::writeLateFieldsToEditItem() {
- return;
-}
-
-void BoilEditor::readFieldsFromEditItem([[maybe_unused]] std::optional propName) {
- return;
-}
-
// Insert the boilerplate stuff that we cannot do in EditorWithRecipeBase
-EDITOR_WITH_RECIPE_COMMON_CODE(BoilEditor)
+EDITOR_COMMON_CODE(Boil)
diff --git a/src/editors/BoilEditor.h b/src/editors/BoilEditor.h
index d18531b9..8564e13f 100644
--- a/src/editors/BoilEditor.h
+++ b/src/editors/BoilEditor.h
@@ -23,9 +23,10 @@
#include "ui_boilEditor.h"
-#include "editors/EditorWithRecipeBase.h"
+#include "editors/EditorBase.h"
#include "model/Boil.h"
+#define BoilEditorOptions EditorBaseOptions{ .recipe = true }
/*!
* \class BoilEditor
*
@@ -33,10 +34,12 @@
*
* See also \c NamedBoilEditor
*/
-class BoilEditor : public QDialog, public Ui::boilEditor, public EditorWithRecipeBase {
+class BoilEditor : public QDialog,
+ public Ui::boilEditor,
+ public EditorBase {
Q_OBJECT
- EDITOR_WITH_RECIPE_COMMON_DECL(Boil)
+ EDITOR_COMMON_DECL(Boil, BoilEditorOptions)
};
#endif
diff --git a/src/editors/BoilStepEditor.cpp b/src/editors/BoilStepEditor.cpp
index 418f4ab6..bdbc68e8 100644
--- a/src/editors/BoilStepEditor.cpp
+++ b/src/editors/BoilStepEditor.cpp
@@ -18,67 +18,28 @@
#include "MainWindow.h"
#include "measurement/Unit.h"
-BoilStepEditor::BoilStepEditor(QWidget* parent) :
+BoilStepEditor::BoilStepEditor(QWidget* parent, QString const editorName) :
QDialog{parent},
- EditorBase() {
+ EditorBase(editorName) {
this->setupUi(this);
-
- // NB: label_description / textEdit_description don't need initialisation here as neither is a smart field
- SMART_FIELD_INIT(BoilStepEditor, label_name , lineEdit_name , BoilStep, PropertyNames:: NamedEntity::name );
- SMART_FIELD_INIT(BoilStepEditor, label_startTemp , lineEdit_startTemp , BoilStep, PropertyNames:: Step::startTemp_c , 1);
- SMART_FIELD_INIT(BoilStepEditor, label_stepTime , lineEdit_stepTime , BoilStep, PropertyNames:: Step::stepTime_mins , 0);
- SMART_FIELD_INIT(BoilStepEditor, label_rampTime , lineEdit_rampTime , BoilStep, PropertyNames:: Step::rampTime_mins , 0);
- SMART_FIELD_INIT(BoilStepEditor, label_endTemp , lineEdit_endTemp , BoilStep, PropertyNames:: Step::endTemp_c , 1);
- SMART_FIELD_INIT(BoilStepEditor, label_startAcidity , lineEdit_startAcidity , BoilStep, PropertyNames:: Step::startAcidity_pH, 1);
- SMART_FIELD_INIT(BoilStepEditor, label_endAcidity , lineEdit_endAcidity , BoilStep, PropertyNames:: Step::endAcidity_pH , 1);
- SMART_FIELD_INIT(BoilStepEditor, label_startGravity , lineEdit_startGravity , BoilStep, PropertyNames::StepExtended::startGravity_sg, 3);
- SMART_FIELD_INIT(BoilStepEditor, label_endGravity , lineEdit_endGravity , BoilStep, PropertyNames::StepExtended::endGravity_sg , 3);
-
- BT_COMBO_BOX_INIT(BoilStepEditor, comboBox_boilStepChillingType, BoilStep, chillingType);
-
- this->connectSignalsAndSlots();
+ this->postSetupUiInit({
+ EDITOR_FIELD_NORM(BoilStep, label_name , lineEdit_name , NamedEntity::name ),
+ EDITOR_FIELD_NORM(BoilStep, label_description , textEdit_description , Step::description ),
+ EDITOR_FIELD_NORM(BoilStep, label_startTemp , lineEdit_startTemp , Step::startTemp_c , 1),
+ EDITOR_FIELD_NORM(BoilStep, label_stepTime , lineEdit_stepTime , Step::stepTime_mins , 0),
+ EDITOR_FIELD_NORM(BoilStep, label_rampTime , lineEdit_rampTime , Step::rampTime_mins , 0),
+ EDITOR_FIELD_NORM(BoilStep, label_endTemp , lineEdit_endTemp , Step::endTemp_c , 1),
+ EDITOR_FIELD_NORM(BoilStep, label_startAcidity , lineEdit_startAcidity , Step::startAcidity_pH , 1),
+ EDITOR_FIELD_NORM(BoilStep, label_endAcidity , lineEdit_endAcidity , Step::endAcidity_pH , 1),
+ EDITOR_FIELD_NORM(BoilStep, label_startGravity , lineEdit_startGravity , StepExtended::startGravity_sg, 3),
+ EDITOR_FIELD_NORM(BoilStep, label_endGravity , lineEdit_endGravity , StepExtended::endGravity_sg , 3),
+ EDITOR_FIELD_ENUM(BoilStep, label_boilStepChillingType, comboBox_boilStepChillingType, BoilStep::chillingType ),
+ });
return;
}
BoilStepEditor::~BoilStepEditor() = default;
-void BoilStepEditor::readFieldsFromEditItem(std::optional propName) {
- if (!propName || *propName == PropertyNames:: NamedEntity::name ) { this->lineEdit_name ->setTextCursor(m_editItem->name ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames:: Step::description ) { this->textEdit_description ->setPlainText (m_editItem->description ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames:: Step::startTemp_c ) { this->lineEdit_startTemp ->setQuantity (m_editItem->startTemp_c ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames:: Step::stepTime_mins ) { this->lineEdit_stepTime ->setQuantity (m_editItem->stepTime_mins ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames:: Step::rampTime_mins ) { this->lineEdit_rampTime ->setQuantity (m_editItem->rampTime_mins ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames:: Step::endTemp_c ) { this->lineEdit_endTemp ->setQuantity (m_editItem->endTemp_c ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames:: Step::startAcidity_pH) { this->lineEdit_startAcidity->setQuantity (m_editItem->startAcidity_pH()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames:: Step::endAcidity_pH ) { this->lineEdit_endAcidity ->setQuantity (m_editItem->endAcidity_pH ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::StepExtended::startGravity_sg) { this->lineEdit_startGravity->setQuantity (m_editItem->startGravity_sg()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::StepExtended::endGravity_sg ) { this->lineEdit_endGravity ->setQuantity (m_editItem->endGravity_sg ()); if (propName) { return; } }
-
- if (!propName || *propName == PropertyNames::BoilStep::chillingType ) { this->comboBox_boilStepChillingType->setValue(m_editItem->chillingType()); if (propName) { return; } }
- return;
-}
-
-void BoilStepEditor::writeFieldsToEditItem() {
- this->m_editItem->setName (this->lineEdit_name ->text ());
- this->m_editItem->setDescription (this->textEdit_description ->toPlainText ());
- this->m_editItem->setStartTemp_c (this->lineEdit_startTemp ->getOptCanonicalQty());
- this->m_editItem->setStepTime_mins (this->lineEdit_stepTime ->getOptCanonicalQty());
- this->m_editItem->setRampTime_mins (this->lineEdit_rampTime ->getOptCanonicalQty());
- this->m_editItem->setEndTemp_c (this->lineEdit_endTemp ->getOptCanonicalQty());
- this->m_editItem->setStartAcidity_pH(this->lineEdit_startAcidity->getOptCanonicalQty());
- this->m_editItem->setEndAcidity_pH (this->lineEdit_endAcidity ->getOptCanonicalQty());
- this->m_editItem->setStartGravity_sg(this->lineEdit_startGravity->getOptCanonicalQty());
- this->m_editItem->setEndGravity_sg (this->lineEdit_endGravity ->getOptCanonicalQty());
-
- this->m_editItem->setChillingType(this->comboBox_boilStepChillingType->getOptValue());
- return;
-}
-
-void BoilStepEditor::writeLateFieldsToEditItem() {
- // Nothing to do here
- return;
-}
-
// Insert the boiler-plate stuff that we cannot do in EditorBase
-EDITOR_COMMON_CODE(BoilStepEditor)
+EDITOR_COMMON_CODE(BoilStep)
diff --git a/src/editors/BoilStepEditor.h b/src/editors/BoilStepEditor.h
index 7e64c29d..0d46f925 100644
--- a/src/editors/BoilStepEditor.h
+++ b/src/editors/BoilStepEditor.h
@@ -24,16 +24,18 @@
#include "editors/EditorBase.h"
#include "model/BoilStep.h"
+#define BoilStepEditorOptions EditorBaseOptions{ }
/*!
* \class BoilStepEditor
*
* \brief View/controller dialog for editing boil steps.
*/
-class BoilStepEditor : public QDialog, public Ui::boilStepEditor, public EditorBase {
+class BoilStepEditor : public QDialog,
+ public Ui::boilStepEditor,
+ public EditorBase {
Q_OBJECT
- EDITOR_COMMON_DECL(BoilStep)
-
+ EDITOR_COMMON_DECL(BoilStep, BoilStepEditorOptions)
};
#endif
diff --git a/src/editors/EditorBase.h b/src/editors/EditorBase.h
index 80ca37dd..1e378718 100644
--- a/src/editors/EditorBase.h
+++ b/src/editors/EditorBase.h
@@ -24,113 +24,54 @@
#include
#include
#include
+#include
+#include "BtHorizontalTabs.h"
#include "database/ObjectStoreWrapper.h"
+#include "editors/EditorBaseField.h"
#include "model/NamedEntity.h"
+#include "model/Recipe.h" // Need to include this this to be able to cast Recipe to QObject
#include "utils/CuriouslyRecurringTemplateBase.h"
/**
- * \brief Field info for a field of a subclass of \c EditorBase.
+ * \brief This is used as a template parameter to turn on and off various \b small features in \c EditorBase (in
+ * conjunction with the concepts defined below).
*
- * Note that we can't put this inside the \c EditorBase class declaration as we also want to use it there, and
- * we'd get errors about "invalid use of incomplete type ‘class EditorBase’".
+ * \sa EditorBase
*/
-struct EditorBaseField {
+struct EditorBaseOptions {
/**
- * \brief Most fields are written together. However, some are marked 'Late' because they need to be written after
- * the object is created.
+ * \brief Enabling this turns on the temporary live copy of edit item, whose fields are updated straight away as
+ * edits are made. This is useful for showing calculated values or drawing charts.
*/
- enum class WhenToWrite {
- Normal,
- Late
- };
-
- char const * labelName;
- std::variant label;
- // We need to know what type the field is, partly because QLineEdit and QTextEdit don't have a useful common base
- // class, and partly because we want to access member functions of SmartLineEdit that don't exist on QLineEdit or
- // QTextEdit.
- std::variant editField;
- BtStringConst const & property;
- // Both the next two fields have defaults, but precision is the one that more often needs something other than
- // default to be specified, so we put it first.
- std::optional precision = std::nullopt;
- EditorBaseField::WhenToWrite whenToWrite = WhenToWrite::Normal;
-
- //! Constructor for when we don't have a SmartLineEdit
- template
- EditorBaseField([[maybe_unused]] char const * const editorClass,
- char const * const labelName,
- [[maybe_unused]] char const * const labelFqName,
- LabelType * label,
- [[maybe_unused]] char const * const editFieldName,
- [[maybe_unused]] char const * const editFieldFqName,
- EditFieldType * editField,
- BtStringConst const & property,
- [[maybe_unused]] TypeInfo const & typeInfo,
- std::optional precision = std::nullopt,
- EditorBaseField::WhenToWrite whenToWrite = WhenToWrite::Normal) :
- labelName {labelName },
- label {label },
- editField {editField },
- property {property },
- precision {precision },
- whenToWrite{whenToWrite} {
- return;
- }
-
- //! Constructor for when we have a SmartLineEdit
- template
- EditorBaseField(char const * const editorClass,
- char const * const labelName,
- char const * const labelFqName,
- LabelType * label,
- char const * const editFieldName,
- char const * const editFieldFqName,
- SmartLineEdit * editField,
- BtStringConst const & property,
- TypeInfo const & typeInfo,
- std::optional precision = std::nullopt,
- EditorBaseField::WhenToWrite whenToWrite = WhenToWrite::Normal) :
- labelName {labelName },
- label {label },
- editField {editField },
- property {property },
- precision {precision },
- whenToWrite{whenToWrite} {
- SmartAmounts::Init(editorClass,
- labelName,
- labelFqName,
- *label,
- editFieldName,
- editFieldFqName,
- *editField,
- typeInfo,
- precision);
- return;
- }
-
+ bool liveEditItem = false;
+ /**
+ * \brief Enabling this turns on the automatic update of the first tab to show the name of the item being edited.
+ * This relies on the \c Derived class having a \c QTabWidget called \c tabWidget_editor (and that the object
+ * name is provided by the \c PropertyNames::NamedEntity::name property).
+ */
+ bool nameTab = false;
+ /**
+ * \brief Enabling this turns on the observation of (the current) \c Recipe. Typically it makes sense for us to be
+ * able to watch a \c Recipe when it can have at most one of the thing we are editing (\c Boil, \c Equipment,
+ * \c Fermentation, \c Mash, \c Style).
+ */
+ bool recipe = false;
+ /**
+ * \brief Enabling this means we show the edit item's ID (obtained from the \c NamedEntity::key() function) in a
+ * label field called \c label_id_value.
+ */
+ bool idDisplay = false;
};
-
-/**
- * \brief This macro is similar to SMART_FIELD_INIT, but allows us to pass the EditorBaseField constructor.
- *
- * We assume that, where Foo is some subclass of NamedEntity, then the editor class for Foo is always called
- * FooEditor.
- */
-#define EDITOR_FIELD(modelClass, label, editField, property, ...) \
- EditorBaseField{\
- #modelClass "Editor", \
- #label, \
- #modelClass "Editor->" #label, \
- label, \
- #editField, \
- #modelClass "Editor->" #editField, \
- editField, \
- property, \
- modelClass ::typeLookup.getType(property) \
- __VA_OPT__(, __VA_ARGS__) \
- }
+template struct has_LiveEditItem : public std::integral_constant{};
+template struct has_NameTab : public std::integral_constant{};
+template struct has_Recipe : public std::integral_constant{};
+template struct has_IdDisplay : public std::integral_constant{};
+// See comment in utils/TypeTraits.h for definition of CONCEPT_FIX_UP (and why, for now, we need it)
+template concept CONCEPT_FIX_UP HasLiveEditItem = has_LiveEditItem::value;
+template concept CONCEPT_FIX_UP HasNameTab = has_NameTab ::value;
+template concept CONCEPT_FIX_UP HasRecipe = has_Recipe ::value;
+template concept CONCEPT_FIX_UP HasIdDisplay = has_IdDisplay ::value;
/**
* \class EditorBase
@@ -165,45 +106,128 @@ struct EditorBaseField {
* Note that we cannot do the equivalent for the header file declarations because the Qt MOC does not expand
* non-Qt macros.
*
- * The derived class also needs to implement the following substantive member functions that \c EditorBase will
- * call:
- * - \c void \c writeFieldsToEditItem -- Writes most fields from the editor GUI fields into the object being
- * edited
- * - \c void \c writeLateFieldsToEditItem -- Writes any fields that must wait until the object definitely
- * exists in the DB
- * - \c void \c readFieldsFromEditItem -- (Re)read one or all fields from the object into the relevant GUI
- * field(s).
+ * Additionally, derived class needs to have the following \c QPushButton members (typically defined in the .ui
+ * file):
+ * \c pushButton_new, \c pushButton_save, \c pushButton_cancel
*
- * Finally, derived class needs to have the following QPushButton members (typically defined in the .ui file):
- * pushButton_new, pushButton_save, pushButton_cancel
+ * The LiveEditItem template parameter determines whether we keep a "live" copy of whatever is being edited (ie
+ * a copy object to which edits will be applied in real time). This is useful to show fields calculated by the
+ * NE object itself or (as in the case of \c WaterEditor) to feed data to a chart. Subclasses that set
+ * \c editorBaseOptions.liveEditItem to \c true need the following additional private member functions:
+ * - \c void \c postInputFieldModified -- Update any chart following input field modification.
*/
template class EditorPhantom;
-template
+template
class EditorBase : public CuriouslyRecurringTemplateBase {
public:
-
/**
* \brief Constructor
*
+ * Often with CRTP it's good to make the constructor private and Derived a friend, so that only Derived can
+ * call the CRTP base constructor. This stops errors with incorrect inheritance - eg makes a compile error if
+ * we write `class FooEditor : ... public EditorBase` instead of
+ * `class FooEditor : ... public EditorBase`. However, since we want EditorWithRecipeBase to
+ * inherit from EditorBase, we can't do that trick here.
+ *
* Note that we cannot initialise this->m_fields here, as the parameters themselves won't get constructed
* until Derived calls setupUi().
*/
- EditorBase() :
+ EditorBase(QString const editorName) :
+ m_editorName{editorName},
m_fields{nullptr},
- m_editItem{nullptr} {
+ m_editItem{nullptr},
+ m_liveEditItem{nullptr} {
+ return;
+ }
+ ~EditorBase() = default;
+
+ //! No-op version
+ void setupTabs() requires (!HasNameTab) { return; }
+ //! Substantive version
+ void setupTabs() requires (HasNameTab) {
+ this->derived().tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs);
return;
}
- virtual ~EditorBase() = default;
/**
* \brief Derived should call this after calling setupUi
+ *
+ * NOTE that where two fields are linked to the same property (typically where we have an amount field and a
+ * combo box that controls whether that amount is mass/volume/etc) they \b must be adjacent in
+ * initializer_list. (This is because of how we do early break out when only
*/
- void postSetupUiInit(std::initializer_list fields) {
- this->m_fields = std::make_unique>(fields);
+ void postSetupUiInit(std::initializer_list fields) {
+ this->m_fields = std::make_unique>(fields);
+ this->setupTabs();
this->connectSignalsAndSlots();
return;
}
+ //! \brief No-op version
+ void connectLiveEditSignalsAndSlots() requires (!HasLiveEditItem) {
+ return;
+ }
+
+ /**
+ * \brief When we have a live edit item, we want to know each time an input field has been modified so that we can
+ * update the corresponding property of the live edit item. We connect the relevant signal to
+ * Derived::inputFieldModified, which then calls doInputFieldModified
+ */
+ void connectLiveEditSignalsAndSlots() requires HasLiveEditItem {
+ if (this->m_fields) {
+ for (auto const & field : *this->m_fields) {
+ // Using std::visit and a lambda allows us to do generic things on std::variant
+ std::visit(
+ [this](auto&& fieldInfo) {
+ fieldInfo.connectFieldChanged(&this->derived(), &Derived::inputFieldModified);
+ },
+ field
+ );
+ }
+ }
+ return;
+ }
+
+ //! \brief No-op version
+ void doInputFieldModified([[maybe_unused]] QObject const * const signalSender)
+ requires (!HasLiveEditItem) {
+ return;
+ }
+
+ //! \brief Substantive version
+ void doInputFieldModified(QObject const * const signalSender) requires (HasLiveEditItem) {
+ if (this->m_fields && this->m_liveEditItem && signalSender && signalSender->parent() == &this->derived()) {
+ bool foundMatch = false;
+ for (auto const & field : *this->m_fields) {
+ // Using std::visit and a lambda allows us to do generic things on std::variant
+ if (std::visit(
+ // Lambda returns true if we matched the signal sender to this EditorBaseField, false otherwise
+ [this, signalSender](auto&& fieldInfo) {
+ if (signalSender == fieldInfo.editField) {
+ fieldInfo.setPropertyFromEditField(*this->m_liveEditItem);
+ return true;
+ }
+ return false;
+ },
+ field
+ )) {
+ foundMatch = true;
+ break;
+ }
+ }
+
+ if (!foundMatch) {
+ // If we get here, it's probably a coding error but there's no harm in soldiering on
+ qWarning() << Q_FUNC_INFO << "Unrecognised signal sender";
+ return;
+ }
+
+ this->derived().postInputFieldModified();
+ }
+ return;
+ }
+
/**
* \brief Call this at the end of derived class's constructor (in particular, after the call to \c setupUi).
*
@@ -216,6 +240,18 @@ class EditorBase : public CuriouslyRecurringTemplateBase
this->derived().connect(this->derived().pushButton_new , &QAbstractButton::clicked, &this->derived(), &Derived::clickedNew );
this->derived().connect(this->derived().pushButton_save , &QAbstractButton::clicked, &this->derived(), &Derived::saveAndClose );
this->derived().connect(this->derived().pushButton_cancel, &QAbstractButton::clicked, &this->derived(), &Derived::clearAndClose);
+ //
+ this->connectLiveEditSignalsAndSlots();
+ return;
+ }
+
+ //! \brief No-op version
+ void makeLiveEditItem() requires (!HasLiveEditItem) {
+ return;
+ }
+
+ void makeLiveEditItem() requires HasLiveEditItem {
+ this->m_liveEditItem = std::make_unique(*this->m_editItem);
return;
}
@@ -231,8 +267,22 @@ class EditorBase : public CuriouslyRecurringTemplateBase
this->m_editItem = editItem;
if (this->m_editItem) {
this->derived().connect(this->m_editItem.get(), &NamedEntity::changed, &this->derived(), &Derived::changed);
- this->readFromEditItem(std::nullopt);
+ this->readFieldsFromEditItem(std::nullopt);
}
+
+ this->makeLiveEditItem();
+
+ // Comment below about calling this->derived().validateBeforeSave() also applies here
+ this->derived().postSetEditItem();
+ return;
+ }
+
+ /**
+ * \brief \c Derived can override this if there is additional processing to do at the end of \c setEditItem
+ *
+ * This is used, eg, in \c WaterEditor to set up the \c RadarChart
+ */
+ void postSetEditItem() {
return;
}
@@ -274,56 +324,6 @@ class EditorBase : public CuriouslyRecurringTemplateBase
return;
}
- /**
- * \brief If \c fromEditItem is \c true supplied, sets the \c field.editField from the \c field.property of
- * \c this->m_editItem -- ie populates/updates the UI input field from the model object.
- * If \c fromEditItem is \c false, clears the edit field.
- */
- void getProperty(EditorBaseField const & field, bool const fromEditItem = true) {
- QVariant value;
- if (fromEditItem) {
- value = this->m_editItem->property(*field.property);
- } else {
- value = QString{""};
- }
-
- // Usually leave this debug log commented out unless trouble-shooting as it generates a lot of logging
-// qDebug() << Q_FUNC_INFO << field.labelName << "read from" << field.property << "as" << value;
-
- if (std::holds_alternative(field.editField)) {
- std::get(field.editField)->setPlainText(value.toString());
- } else if (std::holds_alternative(field.editField)) {
- std::get(field.editField)->setText(value.toString());
- } else {
- auto sle = std::get(field.editField);
- if (fromEditItem) {
- sle->setFromVariant(value);
- } else {
- sle->setText(value.toString());
- }
- }
- return;
- }
-
- /**
- * \brief Sets \c field.property on \c this->m_editItem to the \c field.editField value -- ie writes back the UI
- * value into the model object.
- */
- void setProperty(EditorBaseField const & field) {
- QVariant val;
- if (std::holds_alternative(field.editField)) {
- val = QVariant::fromValue(std::get(field.editField)->toPlainText());
- } else if (std::holds_alternative(field.editField)) {
- val = QVariant::fromValue(std::get(field.editField)->text());
- } else {
- auto sle = std::get(field.editField);
- val = sle->getAsVariant();
- }
-
- this->m_editItem->setProperty(*field.property, val);
- return;
- }
-
/**
* \brief Subclass should override this if it needs to validate the form before saving happens.
*
@@ -348,11 +348,11 @@ class EditorBase : public CuriouslyRecurringTemplateBase
return;
}
- this->writeNormalFields();
+ this->writeNormalFieldsToEditItem();
if (this->m_editItem->key() < 0) {
ObjectStoreWrapper::insert(this->m_editItem);
}
- this->writeLateFields();
+ this->writeLateFieldsToEditItem();
this->derived().setVisible(false);
return;
@@ -367,36 +367,111 @@ class EditorBase : public CuriouslyRecurringTemplateBase
return;
}
+ //! \brief No-op version
+ void updateNameTabIfNeeded([[maybe_unused]] std::optional propName)
+ requires (!HasNameTab) {
+ return;
+ }
+ //! \brief Substantive version
+ void updateNameTabIfNeeded(std::optional propName) requires (HasNameTab) {
+ if (!propName || *propName == PropertyNames::NamedEntity::name) {
+ this->derived().tabWidget_editor->setTabText(0, this->m_editItem->name());
+ }
+ return;
+ }
+
+ //! \brief No-op version
+ void showId() requires (!HasIdDisplay) { return; }
+ //! \brief Substantive version
+ void showId() requires (HasIdDisplay) {
+ // This label does not have an input field; it just shows the ID of the item
+ this->derived().label_id_value->setText(QString::number(this->m_editItem->key()));
+ return;
+ }
+
+ //! Derived classes can override this for any extra behaviour
+ void postReadFieldsFromEditItem([[maybe_unused]] std::optional propName) { return; }
+
/**
- * \brief Read either one field (if \c propName specified) or all (if it is \c std::nullopt) into the UI from the
+ * \brief (Re)read either one field (if \c propName specified) or all (if it is \c std::nullopt) into the UI from the
* model item.
*/
- void readFromEditItem(std::optional propName) {
- if (this->m_fields) {
+ void readFieldsFromEditItem(std::optional propName) {
+ if (this->m_editItem && this->m_fields) {
+ bool matched = false;
for (auto const & field : *this->m_fields) {
- if (!propName || *propName == field.property) {
- this->getProperty(field);
- if (propName) {
- // Break out here if we were only updating one property
- break;
- }
+ if (std::visit(
+ //
+ // This lambda returns true if we should stop subsequent loop processing, or false if we should carry on
+ // looking at subsequent fields. Because the code inside the lambda cannot directly break out of the
+ // for loop, we have to return this boolean to tell the calling code whether to break out.
+ //
+ [this, &propName, &matched](auto&& fieldInfo) {
+ //
+ // The update rule is simple -- we either update all fields (because no property name is supplied) or
+ // only the field(s) for the supplied property name.
+ //
+ // In most cases, there will only be one field per property name. However, we also have to handle the
+ // case where we have a combo-box that is controlling the physical quantity for another field (eg
+ // whether an input field is mass or volume). By convention, where there is more than one field for a
+ // property name, the records must be adjacent in the m_fields vector. This makes our "break"
+ // criteria relatively simple:
+ // - We have a property name
+ // - We already matched it at least once
+ // - The current field does not match
+ //
+ if (!propName || *propName == fieldInfo.property) {
+ // Normally leave this log statement commented out as it generates too many lines in the log file
+// qDebug() << Q_FUNC_INFO << "Reading" << fieldInfo.property;
+ fieldInfo.setEditFieldFromProperty(*this->m_editItem);
+ if (propName) {
+ matched = true;
+ }
+ } else if (!propName && matched) {
+ return true;
+ }
+ return false;
+ },
+ field
+ )) {
+ break;
}
}
}
- // TODO: For the moment, we still do this call, but ultimately we'll eliminate it.
- this->derived().readFieldsFromEditItem(propName);
+ // The ID is not going to change unless we're reading in all fields
+ if (!propName) {
+ this->showId();
+ }
+ this->updateNameTabIfNeeded(propName);
+ // Note the need for derived() here to allow Derived to override
+ this->derived().postReadFieldsFromEditItem(propName);
return;
}
+ //! No-op version
+ bool handleChangeFromRecipe([[maybe_unused]] QObject * sender) requires (!HasRecipe) {
+ return false;
+ }
+ //! Substantive version
+ bool handleChangeFromRecipe(QObject * sender) requires (HasRecipe) {
+ if (this->m_recipeObs && sender == static_cast(this->m_recipeObs)) {
+ this->readAllFields();
+ return true;
+ }
+ return false;
+ }
/**
* \brief Subclass should call this from its \c changed slot
*
* Note that \c QObject::sender has \c protected access specifier, so we can't call it from here, not even
* via the derived class pointer. Therefore we have derived class call it and pass us the result.
*/
- virtual void doChanged(QObject * sender, QMetaProperty prop, [[maybe_unused]] QVariant val) {
+ void doChanged(QObject * sender, QMetaProperty prop, [[maybe_unused]] QVariant val) {
+ if (this->handleChangeFromRecipe(sender)) {
+ return;
+ }
if (this->m_editItem && sender == this->m_editItem.get()) {
- this->readFromEditItem(prop.name());
+ this->readFieldsFromEditItem(prop.name());
}
return;
}
@@ -404,7 +479,12 @@ class EditorBase : public CuriouslyRecurringTemplateBase
void doClearFields() {
if (this->m_fields) {
for (auto const & field : *this->m_fields) {
- this->getProperty(field, false);
+ std::visit(
+ [](auto&& fieldInfo) {
+ fieldInfo.clearEditField();
+ },
+ field
+ );
}
}
return;
@@ -412,44 +492,85 @@ class EditorBase : public CuriouslyRecurringTemplateBase
void readAllFields() {
if (this->m_editItem) {
- this->readFromEditItem(std::nullopt);
+ this->readFieldsFromEditItem(std::nullopt);
} else {
this->doClearFields();
}
return;
}
- void writeFields(EditorBaseField::WhenToWrite const normalOrLate) {
- if (this->m_fields) {
+ void writeFields(WhenToWriteField const normalOrLate) {
+ if (this->m_editItem && this->m_fields) {
for (auto const & field : *this->m_fields) {
- if (normalOrLate == field.whenToWrite) {
- this->setProperty(field);
- }
+ std::visit(
+ [this, normalOrLate](auto&& fieldInfo) {
+ if (normalOrLate == fieldInfo.whenToWrite) {
+ fieldInfo.setPropertyFromEditField(*this->m_editItem);
+ }
+ },
+ field
+ );
}
}
return;
}
- void writeNormalFields() {
- this->writeFields(EditorBaseField::WhenToWrite::Normal);
- // TODO: For the moment, we still do this call, but ultimately we'll eliminate it.
- this->derived().writeFieldsToEditItem();
+ //! Derived classes can override this for any extra behaviour
+ void postWriteNormalFieldsToEditItem() { return; }
+
+ //! Write most fields from the editor GUI fields into the object being edited
+ void writeNormalFieldsToEditItem() {
+ this->writeFields(WhenToWriteField::Normal);
+ // Note the need for derived() here to allow Derived to override
+ this->derived().postWriteNormalFieldsToEditItem();
+ return;
+ }
+
+ //! Derived classes can override this for any extra behaviour
+ void postWriteLateFieldsToEditItem() { return; }
+
+ //! Write any fields that must wait until the object definitely exists in the DB
+ void writeLateFieldsToEditItem() {
+ this->writeFields(WhenToWriteField::Late);
+ // Note the need for derived() here to allow Derived to override
+ this->derived().postWriteLateFieldsToEditItem();
+ return;
+ }
+
+ void setRecipe(Recipe * recipe) requires HasRecipe {
+ this->m_recipeObs = recipe;
+ // TBD: We could automatically set the edit item as follows:
+// if (this->m_recipeObs) {
+// this->m_editItem = this->m_recipeObs->get()
+// }
+ return;
+ }
+
+ //! \brief Show the editor and re-read the edit item. Used for editors with Recipe.
+ void doShowEditor() {
+ this->readAllFields();
+ this->derived().setVisible(true);
return;
}
- void writeLateFields() {
- this->writeFields(EditorBaseField::WhenToWrite::Late);
- // TODO: For the moment, we still do this call, but ultimately we'll eliminate it.
- this->derived().writeLateFieldsToEditItem();
+ //! \brief Close (hide) the editor without saving anything. Used for editors with Recipe.
+ void doCloseEditor() {
+ this->derived().setVisible(false);
return;
}
protected:
+ /**
+ * \brief Optionally an editor can have a "name" to add some context. Eg for the Water editor, the water chemistry
+ * dialog allows you to have two of them open at once -- one "Base" and one "Target".
+ */
+ QString const m_editorName;
+
/**
* \brief Info about fields in this editor
*/
- std::unique_ptr> m_fields;
+ std::unique_ptr> m_fields;
/**
* \brief This is the \c NamedEntity subclass object we are creating or editing. We are also "observing" it in the
@@ -458,6 +579,25 @@ class EditorBase : public CuriouslyRecurringTemplateBase
* of the editor classes.
*/
std::shared_ptr m_editItem;
+
+ /**
+ * \brief Optionally, an editor can create a temporary copy of \c m_editItem to which to apply edits immediately.
+ * This is useful if we want to be able to show calculated values or if (as in the case of \c WaterEditor) we
+ * want to use a copy object to as input to a chart or graph showing live edits. This object is discarded
+ * when the user clicks Save or Cancel. (In the former case, the form values are applied to \c m_editItem; in
+ * the latter they are not.
+ *
+ * There are various tricks where we could make the existence or type of this member variable depend on the
+ * LEI template parameter (see https://brevzin.github.io/c++/2021/11/21/conditional-members/) but it's
+ * currently a bit complicated, and should become easier with future reflection features. So, for now, we
+ * we don't worry about the overhead of unnecessarily having this member when LEI is LiveEditItem::Disabled.
+ */
+ std::unique_ptr m_liveEditItem;
+
+ /**
+ * \brief The \c Recipe, if any, that we are "observing".
+ */
+ Recipe * m_recipeObs = nullptr;
};
/**
@@ -465,16 +605,14 @@ class EditorBase : public CuriouslyRecurringTemplateBase
*
* Note we have to be careful about comment formats in macro definitions
*/
-#define EDITOR_COMMON_DECL(NeName) \
+#define EDITOR_COMMON_DECL(NeName, Options) \
/* This allows EditorBase to call protected and private members of Derived */ \
- friend class EditorBase; \
+ friend class EditorBase; \
\
public: \
- NeName##Editor(QWidget * parent = nullptr); \
+ NeName##Editor(QWidget * parent = nullptr, QString const editorName = ""); \
virtual ~NeName##Editor(); \
\
- void writeFieldsToEditItem(); \
- void writeLateFieldsToEditItem(); \
void readFieldsFromEditItem(std::optional propName); \
\
public slots: \
@@ -483,14 +621,21 @@ class EditorBase : public CuriouslyRecurringTemplateBase
void clearAndClose(); \
void changed(QMetaProperty, QVariant); \
void clickedNew(); \
+ void inputFieldModified(); \
+ /* Additional standard slots for editors with recipe */ \
+ void showEditor(); \
+ void closeEditor(); \
/**
* \brief Derived classes should include this in their implementation file
*/
-#define EDITOR_COMMON_CODE(EditorName) \
- void EditorName::saveAndClose() { this->doSaveAndClose(); return; } \
- void EditorName::clearAndClose() { this->doClearAndClose(); return; } \
- void EditorName::changed(QMetaProperty prop, QVariant val) { this->doChanged(this->sender(), prop, val); return; } \
- void EditorName::clickedNew() { this->newEditItem(); return;}
+#define EDITOR_COMMON_CODE(NeName) \
+ void NeName##Editor::saveAndClose() { this->doSaveAndClose(); return; } \
+ void NeName##Editor::clearAndClose() { this->doClearAndClose(); return; } \
+ void NeName##Editor::changed(QMetaProperty prop, QVariant val) { this->doChanged(this->sender(), prop, val); return; } \
+ void NeName##Editor::clickedNew() { this->newEditItem(); return; } \
+ void NeName##Editor::inputFieldModified() { this->doInputFieldModified(this->sender()); return; }; \
+ void NeName##Editor::showEditor() { this->doShowEditor(); } \
+ void NeName##Editor::closeEditor() { this->doCloseEditor(); } \
#endif
diff --git a/src/editors/EditorBaseField.h b/src/editors/EditorBaseField.h
new file mode 100644
index 00000000..ab4785a8
--- /dev/null
+++ b/src/editors/EditorBaseField.h
@@ -0,0 +1,414 @@
+/*======================================================================================================================
+ * editors/EditorBaseField.h is part of Brewken, and is copyright the following authors 2024:
+ * • Matt Young
+ *
+ * Brewken 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.
+ *
+ * Brewken 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
+ * .
+ =====================================================================================================================*/
+#ifndef EDITORS_EDITORBASEFIELD_H
+#define EDITORS_EDITORBASEFIELD_H
+#pragma once
+
+#include
+
+#include "widgets/BtBoolComboBox.h"
+#include "widgets/BtComboBox.h"
+
+//
+// This is only included from one place -- editors/EditorBase.h -- but I think it's a big enough block that there is
+// benefit in having it as a separate file.
+//
+
+/**
+ * \brief Most fields are written together. However, some are marked 'Late' because they need to be written after
+ * the object is created.
+ *
+ * Logically this belongs inside EditorBaseField, but that's templated, so it would get a bit hard to refer to if
+ * we put it there.
+ */
+enum class WhenToWriteField {
+ Normal,
+ Late,
+ Never
+};
+
+/**
+ * \brief Field info for a field of a subclass of \c EditorBase.
+ *
+ * Note that we can't put this inside the \c EditorBase class declaration as we also want to use it there, and
+ * we'd get errors about "invalid use of incomplete type ‘class EditorBase’".
+ *
+ * We template on both label and edit field types. This is partly so we call the right overload of
+ * \c SmartAmounts::Init, partly because \c QLineEdit and \c QTextEdit don't have a useful common base class, and
+ * partly because we want to access member functions of \c SmartLineEdit that don't exist on \c QLineEdit or
+ * \c QTextEdit.
+ *
+ * Note that the member functions are mostly \c const because they are not modifying this struct -- merely things
+ * referenced by the struct.
+ */
+template
+struct EditorBaseField {
+
+ char const * labelName;
+ LabelType * label;
+ EditFieldType * editField;
+ BtStringConst const & property;
+ // This field isn't used for all values of EditFieldType, but we don't try to make it conditional for the same
+ // reasons as EditorBase::m_liveEditItem below.
+ std::optional precision = std::nullopt;
+ WhenToWriteField whenToWrite = WhenToWriteField::Normal;
+ bool hasControlledField = false;
+
+ /**
+ * \brief Constructor for when we don't have a SmartLineEdit or similar
+ *
+ * NB: Both \c precision and \c whenToWrite have defaults, but precision is the one that more often needs
+ * something other than default to be specified, so we put it first in the argument list.
+ */
+ EditorBaseField([[maybe_unused]] char const * const editorClass,
+ char const * const labelName,
+ [[maybe_unused]] char const * const labelFqName,
+ LabelType * label,
+ [[maybe_unused]] char const * const editFieldName,
+ [[maybe_unused]] char const * const editFieldFqName,
+ EditFieldType * editField,
+ BtStringConst const & property,
+ [[maybe_unused]] TypeInfo const & typeInfo,
+ std::optional precision = std::nullopt,
+ WhenToWriteField whenToWrite = WhenToWriteField::Normal)
+ requires (!std::same_as &&
+ !std::same_as &&
+ !std::same_as) :
+ labelName {labelName },
+ label {label },
+ editField {editField },
+ property {property },
+ precision {precision },
+ whenToWrite{whenToWrite} {
+ return;
+ }
+
+ //! Constructor for when we have a SmartLineEdit
+ EditorBaseField(char const * const editorClass,
+ char const * const labelName,
+ char const * const labelFqName,
+ LabelType * label,
+ char const * const editFieldName,
+ char const * const editFieldFqName,
+ SmartLineEdit * editField,
+ BtStringConst const & property,
+ TypeInfo const & typeInfo,
+ std::optional precision = std::nullopt,
+ WhenToWriteField whenToWrite = WhenToWriteField::Normal)
+ requires (std::same_as) :
+ labelName {labelName },
+ label {label },
+ editField {editField },
+ property {property },
+ precision {precision },
+ whenToWrite{whenToWrite} {
+ SmartAmounts::Init(editorClass,
+ labelName,
+ labelFqName,
+ *label,
+ editFieldName,
+ editFieldFqName,
+ *editField,
+ typeInfo,
+ precision);
+ return;
+ }
+
+ //! Constructor for when we have a BtComboBox
+ EditorBaseField(char const * const editorClass,
+ char const * const labelName,
+ [[maybe_unused]] char const * const labelFqName,
+ LabelType * label,
+ char const * const editFieldName,
+ char const * const editFieldFqName,
+ BtComboBox * editField,
+ BtStringConst const & property,
+ TypeInfo const & typeInfo,
+ EnumStringMapping const & nameMapping,
+ EnumStringMapping const & displayNameMapping,
+ std::vector const * restrictTo = nullptr,
+ SmartLineEdit * controlledField = nullptr,
+ WhenToWriteField whenToWrite = WhenToWriteField::Normal)
+ requires (std::same_as) :
+ labelName {labelName },
+ label {label },
+ editField {editField },
+ property {property },
+ precision {std::nullopt},
+ whenToWrite{whenToWrite} {
+ editField->init(editorClass,
+ editFieldName,
+ editFieldFqName,
+ nameMapping,
+ displayNameMapping,
+ typeInfo,
+ restrictTo,
+ controlledField);
+ if (controlledField) {
+ this->hasControlledField = true;
+ }
+ return;
+ }
+
+ //! Constructor for when we have a BtBoolComboBox
+ EditorBaseField(char const * const editorClass,
+ char const * const labelName,
+ [[maybe_unused]] char const * const labelFqName,
+ LabelType * label,
+ char const * const editFieldName,
+ char const * const editFieldFqName,
+ BtBoolComboBox * editField,
+ BtStringConst const & property,
+ TypeInfo const & typeInfo,
+ QString const & unsetDisplay = QObject::tr("No"),
+ QString const & setDisplay = QObject::tr("Yes"),
+ WhenToWriteField whenToWrite = WhenToWriteField::Normal)
+ requires (std::same_as) :
+ labelName {labelName },
+ label {label },
+ editField {editField },
+ property {property },
+ precision {std::nullopt},
+ whenToWrite{whenToWrite} {
+ // We could use BT_BOOL_COMBO_BOX_INIT here, but we'd be repeating a bunch of work we already did in EDITOR_FIELD
+ editField->init(editorClass,
+ editFieldName,
+ editFieldFqName,
+ unsetDisplay,
+ setDisplay,
+ typeInfo);
+ return;
+ }
+
+ //
+ // You might think that in these connectFieldChanged, it would suffice to use QObject * as the type of context, but
+ // this gave an error about "invalid conversion from ‘QObject*’ to
+ // ‘const QtPrivate::FunctionPointer::Object*’ {aka ‘const WaterEditor*’} [-fpermissive]".
+ // Rather than fight this, we just add another template parameter.
+ //
+
+ //! Simple case - the field tells us editing finished via the \c editingFinished signal
+ template
+ void connectFieldChanged(Derived * context, Functor functor) const
+ requires (std::same_as) {
+ // We ignore the defaulted parameter on connect, and its return value, as we don't need them
+ context->connect(this->editField, &EditFieldType::editingFinished, context, functor, Qt::AutoConnection);
+ return;
+ }
+
+ //! I don't know \c QPlainTextEdit does not have an \c editingFinished signal
+ template
+ void connectFieldChanged(Derived * context, Functor functor) const
+ requires (std::same_as ||
+ std::same_as) {
+ context->connect(this->editField, &EditFieldType::textChanged, context, functor, Qt::AutoConnection);
+ return;
+ }
+
+ //! \c SmartLineEdit uses \c editingFinished itself, and subsequently emits \c textModified after text corrections
+ template
+ void connectFieldChanged(Derived * context, Functor functor) const
+ requires (std::same_as) {
+ context->connect(this->editField, &EditFieldType::textModified, context, functor, Qt::AutoConnection);
+ return;
+ }
+
+ //! Combo boxes are slightly different
+ template
+ void connectFieldChanged(Derived * context, Functor functor) const
+ requires (std::same_as ||
+ std::same_as) {
+ // QOverload is needed on next line because the signal currentIndexChanged is overloaded in QComboBox - see
+ // https://doc.qt.io/qt-5/qcombobox.html#currentIndexChanged
+ context->connect(this->editField, QOverload::of(&QComboBox::currentIndexChanged), context, functor, Qt::AutoConnection);
+ return;
+ }
+
+ QVariant getFieldValue() const requires (std::same_as ||
+ std::same_as) {
+ return this->editField->toPlainText();
+ }
+
+ QVariant getFieldValue() const requires (std::same_as) {
+ return this->editField->text();
+ }
+
+ QVariant getFieldValue() const requires (std::same_as ||
+ std::same_as ||
+ std::same_as) {
+ // Through the magic of templates, and naming conventions, one line suffices for all three types
+ return this->editField->getAsVariant();
+ }
+
+ /**
+ * \brief Set property on supplied object from edit field
+ */
+ void setPropertyFromEditField(QObject & object) const {
+ //
+ // The only "special case" we can't handle with template specialisation is where we have a combo-box that is
+ // controlling the physical quantity for another field (eg whether an input field is mass or volume), there is
+ // nothing to do here (because the Amount returned from the controlled field holds the units that we need to pass
+ // to the object setter).
+ //
+ if (!this->hasControlledField) {
+ object.setProperty(*property, this->getFieldValue());
+ }
+ return;
+ }
+
+ void setEditFieldText(QString const & val) const requires (std::same_as ||
+ std::same_as) {
+ this->editField->setPlainText(val);
+ return;
+ }
+
+ void setEditFieldText(QString const & val) const requires (std::same_as ||
+ std::same_as) {
+ this->editField->setText(val);
+ return;
+ }
+
+ void setEditField(QVariant const & val) const requires (std::same_as ||
+ std::same_as ||
+ std::same_as) {
+ this->setEditFieldText(val.toString());
+ return;
+ }
+
+ void setEditField(QVariant const & val) const requires (std::same_as ||
+ std::same_as ||
+ std::same_as) {
+ this->editField->setFromVariant(val);
+ return;
+ }
+
+ //! This clears the field, or sets it to the default value
+ void clearEditField() const requires (std::same_as ||
+ std::same_as ||
+ std::same_as) {
+ this->setEditFieldText("");
+ return;
+ }
+ void clearEditField() const requires (std::same_as ||
+ std::same_as ||
+ std::same_as) {
+ this->editField->setDefault();
+ return;
+ }
+
+ /**
+ * \brief Set edit field from property on supplied object
+ */
+ void setEditFieldFromProperty(QObject & object) const requires (std::same_as) {
+ //
+ // Similarly to setPropertyFromEditField, in the case of a combo-box that is controlling the physical quantity for
+ // another field, we want to initialise from that controlled field.
+ //
+ if (this->hasControlledField) {
+ this->editField->autoSetFromControlledField();
+ } else {
+ this->setEditField(object.property(*property));
+ }
+ return;
+ }
+ void setEditFieldFromProperty(QObject & object) const requires (!std::same_as) {
+ this->setEditField(object.property(*property));
+ return;
+ }
+
+};
+
+using EditorBaseFieldVariant = std::variant<
+ // Not all permutations are valid, hence why some are commented out
+ EditorBaseField,
+ EditorBaseField,
+ EditorBaseField,
+ EditorBaseField,
+ EditorBaseField,
+ EditorBaseField,
+ EditorBaseField, // This is for tabs such as tab_notes containing a single QTextEdit with no separate QLabel
+ EditorBaseField,
+// EditorBaseField,
+// EditorBaseField,
+ EditorBaseField,
+ EditorBaseField,
+ EditorBaseField
+>;
+
+/**
+ * \brief These macros are similar to SMART_FIELD_INIT, but allows us to pass the EditorBaseField constructor.
+ *
+ * We assume that, where Foo is some subclass of NamedEntity, then the editor class for Foo is always called
+ * FooEditor.
+ *
+ * Note that we can't just write decltype(*label) because (as explained at
+ * https://stackoverflow.com/questions/34231547/decltype-a-dereferenced-pointer-in-c), *label is actually a
+ * reference, and we can't have a member of EditorBaseField be a pointer to a reference. Fortunately
+ * std::remove_pointer does what we want.
+ *
+ * \c EDITOR_FIELD_NORM should be used for most fields
+ * \c EDITOR_FIELD_ENUM is for a combo box for an enum
+ * \c EDITOR_FIELD_COPQ is for a combo box that controls the physical quantity (eg mass/volume) of another field
+ *
+ * To minimise errors, we try to keep the invocations of \c EDITOR_FIELD_NORM, \c EDITOR_FIELD_ENUM and
+ * \c EDITOR_FIELD_COPQ similar to each other -- within the limits of what we can do in macros. This pushes us
+ * to using \c NamedEntity::name etc rather than \c PropertyNames::NamedEntity::name for the fourth parameter,
+ * because in the _ENUM version, we're also going to use this to get the StringMapping and DisplayNames lookups
+ * via the naming conventions we use for those. (I did think about whether we should go beyond just a naming
+ * convention for those sets of look-ups, but it feels like it would be adding a reasonable amount of extra
+ * complexity for very small benefit.)
+ *
+ * NOTE that we cannot completely check the first parameter at compile-time. If you, eg, accidentally put
+ * \c Fermentation when you mean \c FermentationStep, the code will compile, but you will get an assert at
+ * run-time (when we try to look up a \c FermentationStep property type info on \c Fermentation).
+ */
+#define EDITOR_FIELD_BEGIN(modelClass, label, editField, property) \
+ EditorBaseFieldVariant{ \
+ EditorBaseField::type, std::remove_pointer::type>{\
+ #modelClass "Editor", \
+ #label, \
+ #modelClass "Editor->" #label, \
+ label, \
+ #editField, \
+ #modelClass "Editor->" #editField, \
+ editField, \
+ PropertyNames::property, \
+ modelClass ::typeLookup.getType(PropertyNames::property)
+
+#define EDITOR_FIELD_END(...) \
+ __VA_OPT__(, __VA_ARGS__) \
+ } \
+ }
+
+#define EDITOR_FIELD_NORM(modelClass, label, editField, property, ...) \
+ EDITOR_FIELD_BEGIN(modelClass, label, editField, property) \
+ EDITOR_FIELD_END(__VA_ARGS__)
+
+#define EDITOR_FIELD_ENUM(modelClass, label, editField, property, ...) \
+ EDITOR_FIELD_BEGIN(modelClass, label, editField, property), \
+ property##StringMapping, \
+ property##DisplayNames \
+ EDITOR_FIELD_END(__VA_ARGS__)
+
+#define EDITOR_FIELD_COPQ(modelClass, label, editField, property, controlledField, ...) \
+ EDITOR_FIELD_BEGIN(modelClass, label, editField, property), \
+ Measurement::physicalQuantityStringMapping, \
+ Measurement::physicalQuantityDisplayNames, \
+ &Measurement::allPossibilitiesAsInt(modelClass::validMeasures), \
+ controlledField \
+ EDITOR_FIELD_END(__VA_ARGS__)
+
+#endif
diff --git a/src/editors/EditorWithRecipeBase.h b/src/editors/EditorWithRecipeBase.h
deleted file mode 100644
index 5da6e9b6..00000000
--- a/src/editors/EditorWithRecipeBase.h
+++ /dev/null
@@ -1,119 +0,0 @@
-/*======================================================================================================================
- * editors/EditorWithRecipeBase.h is part of Brewken, and is copyright the following authors 2024:
- * • Matt Young
- *
- * Brewken 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.
- *
- * Brewken 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
- * .
- =====================================================================================================================*/
-#ifndef EDITORS_EDITORWITHRECIPEBASE_H
-#define EDITORS_EDITORWITHRECIPEBASE_H
-#pragma once
-
-#include "editors/EditorBase.h"
-#include "model/Recipe.h"
-
-/**
- * \brief Extends \c EditorBase for editors where it makes sense for us to be able to watch a \c Recipe, because a
- * \c Recipe can have at most one of the thing we are editing (\c Boil, \c Equipment, \c Fermentation, \c Mash,
- * \c Style).
- *
- * Classes deriving from this one have the same requirements as those deriving directly from \c EditorBase,
- * except that:
- * EDITOR_WITH_RECIPE_COMMON_DECL should be placed in the header file instead of EDITOR_COMMON_DECL
- * EDITOR_WITH_RECIPE_COMMON_CODE should be placed in the .cpp file instead of EDITOR_COMMON_CODE
- */
-template
-class EditorWithRecipeBase : public EditorBase {
-public:
- EditorWithRecipeBase() :
- EditorBase{},
- m_recipeObs{nullptr} {
- return;
- }
- virtual ~EditorWithRecipeBase() = default;
-
- void setRecipe(Recipe * recipe) {
- this->m_recipeObs = recipe;
- // TBD: We could automatically set the edit item as follows:
-// if (this->m_recipeObs) {
-// this->m_editItem = this->m_recipeObs->get()
-// }
- return;
- }
-
- /**
- * \brief Override \c EditorBase::doChanged
- */
- virtual void doChanged(QObject * sender, QMetaProperty prop, QVariant val) {
- // Extra handling if sender is Recipe we are observing...
- if (this->m_recipeObs && sender == this->m_recipeObs) {
- this->readAllFields();
- return;
- }
- // ...otherwise we fall back to the base class handling
- this->EditorBase::doChanged(sender, prop, val);
- return;
- }
-
- /**
- * \brief
- */
- void doShowEditor() {
- this->readAllFields();
- this->derived().setVisible(true);
- return;
- }
-
- /**
- * \brief
- */
- void doCloseEditor() {
- this->derived().setVisible(true);
- return;
- }
-
-protected:
-
- /**
- * \brief The \c Recipe, if any, that we are "observing".
- */
- Recipe * m_recipeObs;
-};
-
-/**
- * \brief Derived classes should include this in their header file, right after Q_OBJECT, instead of EDITOR_COMMON_DECL
- * (which this macro also pulls in).
- *
- * Note we have to be careful about comment formats in macro definitions
- */
-#define EDITOR_WITH_RECIPE_COMMON_DECL(NeName) \
- EDITOR_COMMON_DECL(NeName) \
- \
- /* This allows EditorWithRecipeBase to call protected and private members of Derived */ \
- friend class EditorWithRecipeBase; \
- \
- public slots: \
- /* Additional standard slots for editors with recipe */ \
- void showEditor(); \
- void closeEditor(); \
-
-/**
- * \brief Derived classes should include this in their implementation file, usually at the end, instead of
- * EDITOR_COMMON_CODE (which this macro also pulls in).
- */
-#define EDITOR_WITH_RECIPE_COMMON_CODE(EditorName) \
- EDITOR_COMMON_CODE(EditorName) \
- void EditorName::showEditor() { this->doShowEditor(); return; } \
- void EditorName::closeEditor() { this->doCloseEditor(); return; } \
-
-
-
-#endif
diff --git a/src/editors/EquipmentEditor.cpp b/src/editors/EquipmentEditor.cpp
index 2e8e21e0..7f5c7e5d 100644
--- a/src/editors/EquipmentEditor.cpp
+++ b/src/editors/EquipmentEditor.cpp
@@ -47,68 +47,67 @@
// user to grab this value (and that of other common materials if we can find them).
//
-EquipmentEditor::EquipmentEditor(QWidget* parent/*, bool singleEquipEditor*/) :
+EquipmentEditor::EquipmentEditor(QWidget* parent, QString const editorName) :
QDialog(parent),
- EditorBase() {
+ EditorBase(editorName) {
this->setupUi(this);
-
this->tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs);
-
- SMART_FIELD_INIT(EquipmentEditor, label_name , lineEdit_name , Equipment, PropertyNames::NamedEntity::name );
- SMART_FIELD_INIT(EquipmentEditor, label_mashTunSpecificHeat , lineEdit_mashTunSpecificHeat , Equipment, PropertyNames::Equipment::mashTunSpecificHeat_calGC );
- SMART_FIELD_INIT(EquipmentEditor, label_mashTunGrainAbsorption , lineEdit_mashTunGrainAbsorption , Equipment, PropertyNames::Equipment::mashTunGrainAbsorption_LKg );
- SMART_FIELD_INIT(EquipmentEditor, label_hopUtilization , lineEdit_hopUtilization , Equipment, PropertyNames::Equipment::hopUtilization_pct , 0);
- SMART_FIELD_INIT(EquipmentEditor, label_mashTunWeight , lineEdit_mashTunWeight , Equipment, PropertyNames::Equipment::mashTunWeight_kg );
- SMART_FIELD_INIT(EquipmentEditor, label_boilingPoint , lineEdit_boilingPoint , Equipment, PropertyNames::Equipment::boilingPoint_c , 1);
- SMART_FIELD_INIT(EquipmentEditor, label_boilTime , lineEdit_boilTime , Equipment, PropertyNames::Equipment::boilTime_min );
- SMART_FIELD_INIT(EquipmentEditor, label_fermenterBatchSize , lineEdit_fermenterBatchSize , Equipment, PropertyNames::Equipment::fermenterBatchSize_l );
- SMART_FIELD_INIT(EquipmentEditor, label_kettleBoilSize , lineEdit_kettleBoilSize , Equipment, PropertyNames::Equipment::kettleBoilSize_l );
- SMART_FIELD_INIT(EquipmentEditor, label_kettleEvaporationPerHour, lineEdit_kettleEvaporationPerHour, Equipment, PropertyNames::Equipment::kettleEvaporationPerHour_l );
- SMART_FIELD_INIT(EquipmentEditor, label_lauterTunDeadspaceLoss , lineEdit_lauterTunDeadspaceLoss , Equipment, PropertyNames::Equipment::lauterTunDeadspaceLoss_l );
- SMART_FIELD_INIT(EquipmentEditor, label_topUpKettle , lineEdit_topUpKettle , Equipment, PropertyNames::Equipment::topUpKettle_l );
- SMART_FIELD_INIT(EquipmentEditor, label_topUpWater , lineEdit_topUpWater , Equipment, PropertyNames::Equipment::topUpWater_l );
- SMART_FIELD_INIT(EquipmentEditor, label_kettleTrubChillerLoss , lineEdit_kettleTrubChillerLoss , Equipment, PropertyNames::Equipment::kettleTrubChillerLoss_l );
- SMART_FIELD_INIT(EquipmentEditor, label_mashTunVolume , lineEdit_mashTunVolume , Equipment, PropertyNames::Equipment::mashTunVolume_l );
- // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞
- SMART_FIELD_INIT(EquipmentEditor, label_hltType , lineEdit_hltType , Equipment, PropertyNames::Equipment::hltType );
- SMART_FIELD_INIT(EquipmentEditor, label_mashTunType , lineEdit_mashTunType , Equipment, PropertyNames::Equipment::mashTunType );
- SMART_FIELD_INIT(EquipmentEditor, label_lauterTunType , lineEdit_lauterTunType , Equipment, PropertyNames::Equipment::lauterTunType );
- SMART_FIELD_INIT(EquipmentEditor, label_kettleType , lineEdit_kettleType , Equipment, PropertyNames::Equipment::kettleType );
- SMART_FIELD_INIT(EquipmentEditor, label_fermenterType , lineEdit_fermenterType , Equipment, PropertyNames::Equipment::fermenterType );
- SMART_FIELD_INIT(EquipmentEditor, label_agingVesselType , lineEdit_agingVesselType , Equipment, PropertyNames::Equipment::agingVesselType );
- SMART_FIELD_INIT(EquipmentEditor, label_packagingVesselType , lineEdit_packagingVesselType , Equipment, PropertyNames::Equipment::packagingVesselType );
- SMART_FIELD_INIT(EquipmentEditor, label_hltVolume , lineEdit_hltVolume , Equipment, PropertyNames::Equipment::hltVolume_l );
- SMART_FIELD_INIT(EquipmentEditor, label_lauterTunVolume , lineEdit_lauterTunVolume , Equipment, PropertyNames::Equipment::lauterTunVolume_l );
- SMART_FIELD_INIT(EquipmentEditor, label_agingVesselVolume , lineEdit_agingVesselVolume , Equipment, PropertyNames::Equipment::agingVesselVolume_l );
- SMART_FIELD_INIT(EquipmentEditor, label_packagingVesselVolume , lineEdit_packagingVesselVolume , Equipment, PropertyNames::Equipment::packagingVesselVolume_l );
- SMART_FIELD_INIT(EquipmentEditor, label_hltLoss , lineEdit_hltLoss , Equipment, PropertyNames::Equipment::hltLoss_l );
- SMART_FIELD_INIT(EquipmentEditor, label_mashTunLoss , lineEdit_mashTunLoss , Equipment, PropertyNames::Equipment::mashTunLoss_l );
- SMART_FIELD_INIT(EquipmentEditor, label_fermenterLoss , lineEdit_fermenterLoss , Equipment, PropertyNames::Equipment::fermenterLoss_l );
- SMART_FIELD_INIT(EquipmentEditor, label_agingVesselLoss , lineEdit_agingVesselLoss , Equipment, PropertyNames::Equipment::agingVesselLoss_l );
- SMART_FIELD_INIT(EquipmentEditor, label_packagingVesselLoss , lineEdit_packagingVesselLoss , Equipment, PropertyNames::Equipment::packagingVesselLoss_l );
- SMART_FIELD_INIT(EquipmentEditor, label_kettleOutflowPerMinute , lineEdit_kettleOutflowPerMinute , Equipment, PropertyNames::Equipment::kettleOutflowPerMinute_l );
- SMART_FIELD_INIT(EquipmentEditor, label_hltWeight , lineEdit_hltWeight , Equipment, PropertyNames::Equipment::hltWeight_kg );
- SMART_FIELD_INIT(EquipmentEditor, label_lauterTunWeight , lineEdit_lauterTunWeight , Equipment, PropertyNames::Equipment::lauterTunWeight_kg );
- SMART_FIELD_INIT(EquipmentEditor, label_kettleWeight , lineEdit_kettleWeight , Equipment, PropertyNames::Equipment::kettleWeight_kg );
- SMART_FIELD_INIT(EquipmentEditor, label_hltSpecificHeat , lineEdit_hltSpecificHeat , Equipment, PropertyNames::Equipment::hltSpecificHeat_calGC );
- SMART_FIELD_INIT(EquipmentEditor, label_lauterTunSpecificHeat , lineEdit_lauterTunSpecificHeat , Equipment, PropertyNames::Equipment::lauterTunSpecificHeat_calGC);
- SMART_FIELD_INIT(EquipmentEditor, label_kettleSpecificHeat , lineEdit_kettleSpecificHeat , Equipment, PropertyNames::Equipment::kettleSpecificHeat_calGC );
+ this->postSetupUiInit({
+ EDITOR_FIELD_NORM(Equipment, label_name , lineEdit_name , NamedEntity::name ),
+ EDITOR_FIELD_NORM(Equipment, label_mashTunSpecificHeat , lineEdit_mashTunSpecificHeat , Equipment::mashTunSpecificHeat_calGC ),
+ EDITOR_FIELD_NORM(Equipment, label_mashTunGrainAbsorption , lineEdit_mashTunGrainAbsorption , Equipment::mashTunGrainAbsorption_LKg ),
+ EDITOR_FIELD_NORM(Equipment, label_hopUtilization , lineEdit_hopUtilization , Equipment::hopUtilization_pct , 0),
+ EDITOR_FIELD_NORM(Equipment, label_mashTunWeight , lineEdit_mashTunWeight , Equipment::mashTunWeight_kg ),
+ EDITOR_FIELD_NORM(Equipment, label_boilingPoint , lineEdit_boilingPoint , Equipment::boilingPoint_c , 1),
+ EDITOR_FIELD_NORM(Equipment, label_boilTime , lineEdit_boilTime , Equipment::boilTime_min ),
+ EDITOR_FIELD_NORM(Equipment, label_fermenterBatchSize , lineEdit_fermenterBatchSize , Equipment::fermenterBatchSize_l ),
+ EDITOR_FIELD_NORM(Equipment, label_kettleBoilSize , lineEdit_kettleBoilSize , Equipment::kettleBoilSize_l ),
+ EDITOR_FIELD_NORM(Equipment, label_kettleEvaporationPerHour, lineEdit_kettleEvaporationPerHour, Equipment::kettleEvaporationPerHour_l ),
+ EDITOR_FIELD_NORM(Equipment, label_lauterTunDeadspaceLoss , lineEdit_lauterTunDeadspaceLoss , Equipment::lauterTunDeadspaceLoss_l ),
+ EDITOR_FIELD_NORM(Equipment, label_topUpKettle , lineEdit_topUpKettle , Equipment::topUpKettle_l ),
+ EDITOR_FIELD_NORM(Equipment, label_topUpWater , lineEdit_topUpWater , Equipment::topUpWater_l ),
+ EDITOR_FIELD_NORM(Equipment, label_kettleTrubChillerLoss , lineEdit_kettleTrubChillerLoss , Equipment::kettleTrubChillerLoss_l ),
+ EDITOR_FIELD_NORM(Equipment, label_mashTunVolume , lineEdit_mashTunVolume , Equipment::mashTunVolume_l ),
+ // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞
+ EDITOR_FIELD_NORM(Equipment, label_hltType , lineEdit_hltType , Equipment::hltType ),
+ EDITOR_FIELD_NORM(Equipment, label_mashTunType , lineEdit_mashTunType , Equipment::mashTunType ),
+ EDITOR_FIELD_NORM(Equipment, label_lauterTunType , lineEdit_lauterTunType , Equipment::lauterTunType ),
+ EDITOR_FIELD_NORM(Equipment, label_kettleType , lineEdit_kettleType , Equipment::kettleType ),
+ EDITOR_FIELD_NORM(Equipment, label_fermenterType , lineEdit_fermenterType , Equipment::fermenterType ),
+ EDITOR_FIELD_NORM(Equipment, label_agingVesselType , lineEdit_agingVesselType , Equipment::agingVesselType ),
+ EDITOR_FIELD_NORM(Equipment, label_packagingVesselType , lineEdit_packagingVesselType , Equipment::packagingVesselType ),
+ EDITOR_FIELD_NORM(Equipment, label_hltVolume , lineEdit_hltVolume , Equipment::hltVolume_l ),
+ EDITOR_FIELD_NORM(Equipment, label_lauterTunVolume , lineEdit_lauterTunVolume , Equipment::lauterTunVolume_l ),
+ EDITOR_FIELD_NORM(Equipment, label_agingVesselVolume , lineEdit_agingVesselVolume , Equipment::agingVesselVolume_l ),
+ EDITOR_FIELD_NORM(Equipment, label_packagingVesselVolume , lineEdit_packagingVesselVolume , Equipment::packagingVesselVolume_l ),
+ EDITOR_FIELD_NORM(Equipment, label_hltLoss , lineEdit_hltLoss , Equipment::hltLoss_l ),
+ EDITOR_FIELD_NORM(Equipment, label_mashTunLoss , lineEdit_mashTunLoss , Equipment::mashTunLoss_l ),
+ EDITOR_FIELD_NORM(Equipment, label_fermenterLoss , lineEdit_fermenterLoss , Equipment::fermenterLoss_l ),
+ EDITOR_FIELD_NORM(Equipment, label_agingVesselLoss , lineEdit_agingVesselLoss , Equipment::agingVesselLoss_l ),
+ EDITOR_FIELD_NORM(Equipment, label_packagingVesselLoss , lineEdit_packagingVesselLoss , Equipment::packagingVesselLoss_l ),
+ EDITOR_FIELD_NORM(Equipment, label_kettleOutflowPerMinute , lineEdit_kettleOutflowPerMinute , Equipment::kettleOutflowPerMinute_l ),
+ EDITOR_FIELD_NORM(Equipment, label_hltWeight , lineEdit_hltWeight , Equipment::hltWeight_kg ),
+ EDITOR_FIELD_NORM(Equipment, label_lauterTunWeight , lineEdit_lauterTunWeight , Equipment::lauterTunWeight_kg ),
+ EDITOR_FIELD_NORM(Equipment, label_kettleWeight , lineEdit_kettleWeight , Equipment::kettleWeight_kg ),
+ EDITOR_FIELD_NORM(Equipment, label_hltSpecificHeat , lineEdit_hltSpecificHeat , Equipment::hltSpecificHeat_calGC ),
+ EDITOR_FIELD_NORM(Equipment, label_lauterTunSpecificHeat , lineEdit_lauterTunSpecificHeat , Equipment::lauterTunSpecificHeat_calGC),
+ EDITOR_FIELD_NORM(Equipment, label_kettleSpecificHeat , lineEdit_kettleSpecificHeat , Equipment::kettleSpecificHeat_calGC ),
+ });
// Connect all the boxen
- connect(this->checkBox_showHlt , &QCheckBox::stateChanged , this, &EquipmentEditor::hideOrShowOptionalVessels );
- connect(this->checkBox_showLauterTun , &QCheckBox::stateChanged , this, &EquipmentEditor::hideOrShowOptionalVessels );
- connect(this->checkBox_showAgingVessel , &QCheckBox::stateChanged , this, &EquipmentEditor::hideOrShowOptionalVessels );
- connect(this->checkBox_showPackagingVessel, &QCheckBox::stateChanged , this, &EquipmentEditor::hideOrShowOptionalVessels );
- connect(this->checkBox_defaultEquipment , &QCheckBox::stateChanged , this, &EquipmentEditor::updateDefaultEquipment);
- connect(this->checkBox_calcBoilVolume , &QCheckBox::stateChanged , this, &EquipmentEditor::updateCalcBoilVolume );
- connect(lineEdit_boilTime , &SmartLineEdit::textModified, this, &EquipmentEditor::updateCalcBoilVolume );
- connect(lineEdit_kettleEvaporationPerHour , &SmartLineEdit::textModified, this, &EquipmentEditor::updateCalcBoilVolume );
- connect(lineEdit_topUpWater , &SmartLineEdit::textModified, this, &EquipmentEditor::updateCalcBoilVolume );
- connect(lineEdit_kettleTrubChillerLoss , &SmartLineEdit::textModified, this, &EquipmentEditor::updateCalcBoilVolume );
- connect(lineEdit_fermenterBatchSize , &SmartLineEdit::textModified, this, &EquipmentEditor::updateCalcBoilVolume );
- connect(pushButton_absorption , &QAbstractButton::clicked , this, &EquipmentEditor::resetAbsorption );
-
- this->connectSignalsAndSlots();
+ connect(this->checkBox_showHlt , &QCheckBox::stateChanged , this, &EquipmentEditor::hideOrShowOptionalVessels);
+ connect(this->checkBox_showLauterTun , &QCheckBox::stateChanged , this, &EquipmentEditor::hideOrShowOptionalVessels);
+ connect(this->checkBox_showAgingVessel , &QCheckBox::stateChanged , this, &EquipmentEditor::hideOrShowOptionalVessels);
+ connect(this->checkBox_showPackagingVessel , &QCheckBox::stateChanged , this, &EquipmentEditor::hideOrShowOptionalVessels);
+ connect(this->checkBox_defaultEquipment , &QCheckBox::stateChanged , this, &EquipmentEditor::updateDefaultEquipment );
+ connect(this->checkBox_calcBoilVolume , &QCheckBox::stateChanged , this, &EquipmentEditor::updateCalcBoilVolume );
+ connect(this->lineEdit_boilTime , &SmartLineEdit::textModified, this, &EquipmentEditor::updateCalcBoilVolume );
+ connect(this->lineEdit_kettleEvaporationPerHour , &SmartLineEdit::textModified, this, &EquipmentEditor::updateCalcBoilVolume );
+ connect(this->lineEdit_topUpWater , &SmartLineEdit::textModified, this, &EquipmentEditor::updateCalcBoilVolume );
+ connect(this->lineEdit_kettleTrubChillerLoss , &SmartLineEdit::textModified, this, &EquipmentEditor::updateCalcBoilVolume );
+ connect(this->lineEdit_fermenterBatchSize , &SmartLineEdit::textModified, this, &EquipmentEditor::updateCalcBoilVolume );
+ connect(this->pushButton_absorption , &QAbstractButton::clicked , this, &EquipmentEditor::resetAbsorption );
+
return;
}
@@ -226,119 +225,14 @@ bool EquipmentEditor::validateBeforeSave() {
}
-void EquipmentEditor::writeFieldsToEditItem() {
-
- m_editItem->setName (lineEdit_name ->text ());
- m_editItem->setKettleBoilSize_l (lineEdit_kettleBoilSize ->getNonOptCanonicalQty());
- m_editItem->setFermenterBatchSize_l (lineEdit_fermenterBatchSize ->getNonOptCanonicalQty());
- m_editItem->setMashTunVolume_l (lineEdit_mashTunVolume ->getNonOptCanonicalQty());
- m_editItem->setMashTunWeight_kg (lineEdit_mashTunWeight ->getOptCanonicalQty ());
- m_editItem->setMashTunSpecificHeat_calGC (lineEdit_mashTunSpecificHeat ->getOptCanonicalQty ());
- m_editItem->setBoilTime_min (lineEdit_boilTime ->getOptCanonicalQty ());
- m_editItem->setKettleEvaporationPerHour_l (lineEdit_kettleEvaporationPerHour->getOptCanonicalQty ());
- m_editItem->setTopUpKettle_l (lineEdit_topUpKettle ->getOptCanonicalQty ());
- m_editItem->setTopUpWater_l (lineEdit_topUpWater ->getOptCanonicalQty ());
- m_editItem->setKettleTrubChillerLoss_l (lineEdit_kettleTrubChillerLoss ->getNonOptCanonicalQty());
- m_editItem->setLauterTunDeadspaceLoss_l (lineEdit_lauterTunDeadspaceLoss ->getNonOptCanonicalQty());
- m_editItem->setMashTunGrainAbsorption_LKg (lineEdit_mashTunGrainAbsorption ->getOptCanonicalQty ());
- m_editItem->setBoilingPoint_c (lineEdit_boilingPoint ->getNonOptCanonicalQty());
- m_editItem->setHopUtilization_pct (lineEdit_hopUtilization ->getOptValue ());
- m_editItem->setKettleNotes (textEdit_kettleNotes ->toPlainText ());
- m_editItem->setCalcBoilVolume (checkBox_calcBoilVolume ->isChecked ());
- // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞
- m_editItem->setHltType (lineEdit_hltType ->text ());
- m_editItem->setMashTunType (lineEdit_mashTunType ->text ());
- m_editItem->setLauterTunType (lineEdit_lauterTunType ->text ());
- m_editItem->setKettleType (lineEdit_kettleType ->text ());
- m_editItem->setFermenterType (lineEdit_fermenterType ->text ());
- m_editItem->setAgingVesselType (lineEdit_agingVesselType ->text ());
- m_editItem->setPackagingVesselType (lineEdit_packagingVesselType ->text ());
- m_editItem->setHltVolume_l (lineEdit_hltVolume ->getNonOptCanonicalQty());
- m_editItem->setLauterTunVolume_l (lineEdit_lauterTunVolume ->getNonOptCanonicalQty());
- m_editItem->setAgingVesselVolume_l (lineEdit_agingVesselVolume ->getNonOptCanonicalQty());
- m_editItem->setPackagingVesselVolume_l (lineEdit_packagingVesselVolume ->getNonOptCanonicalQty());
- m_editItem->setHltLoss_l (lineEdit_hltLoss ->getNonOptCanonicalQty());
- m_editItem->setMashTunLoss_l (lineEdit_mashTunLoss ->getNonOptCanonicalQty());
- m_editItem->setFermenterLoss_l (lineEdit_fermenterLoss ->getNonOptCanonicalQty());
- m_editItem->setAgingVesselLoss_l (lineEdit_agingVesselLoss ->getNonOptCanonicalQty());
- m_editItem->setPackagingVesselLoss_l (lineEdit_packagingVesselLoss ->getNonOptCanonicalQty());
- m_editItem->setKettleOutflowPerMinute_l (lineEdit_kettleOutflowPerMinute ->getOptCanonicalQty ());
- m_editItem->setHltWeight_kg (lineEdit_hltWeight ->getOptCanonicalQty ());
- m_editItem->setLauterTunWeight_kg (lineEdit_lauterTunWeight ->getOptCanonicalQty ());
- m_editItem->setKettleWeight_kg (lineEdit_kettleWeight ->getOptCanonicalQty ());
- m_editItem->setHltSpecificHeat_calGC (lineEdit_hltSpecificHeat ->getOptCanonicalQty ());
- m_editItem->setLauterTunSpecificHeat_calGC(lineEdit_lauterTunSpecificHeat ->getOptCanonicalQty ());
- m_editItem->setKettleSpecificHeat_calGC (lineEdit_kettleSpecificHeat ->getOptCanonicalQty ());
- m_editItem->setHltNotes (textEdit_hltNotes ->toPlainText ());
- m_editItem->setMashTunNotes (textEdit_mashTunNotes ->toPlainText ());
- m_editItem->setLauterTunNotes (textEdit_lauterTunNotes ->toPlainText ());
- m_editItem->setFermenterNotes (textEdit_fermenterNotes ->toPlainText ());
- m_editItem->setAgingVesselNotes (textEdit_agingVesselNotes ->toPlainText ());
- m_editItem->setPackagingVesselNotes (textEdit_packagingVesselNotes ->toPlainText ());
-
- return;
-}
-
-void EquipmentEditor::writeLateFieldsToEditItem() {
- // Nothing to do here for Equipment
- return;
-}
-
-void EquipmentEditor::readFieldsFromEditItem(std::optional propName) {
-
- if (!propName || *propName == PropertyNames::NamedEntity::name ) { this->lineEdit_name ->setTextCursor(m_editItem->name ()); // Continues to next line
- /* this->tabWidget_editor->setTabText(0, m_editItem->name()); */ if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::kettleBoilSize_l ) { this->lineEdit_kettleBoilSize ->setQuantity (m_editItem->kettleBoilSize_l ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::fermenterBatchSize_l ) { this->lineEdit_fermenterBatchSize ->setQuantity (m_editItem->fermenterBatchSize_l ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::mashTunVolume_l ) { this->lineEdit_mashTunVolume ->setQuantity (m_editItem->mashTunVolume_l ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::mashTunWeight_kg ) { this->lineEdit_mashTunWeight ->setQuantity (m_editItem->mashTunWeight_kg ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::mashTunSpecificHeat_calGC ) { this->lineEdit_mashTunSpecificHeat ->setQuantity (m_editItem->mashTunSpecificHeat_calGC ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::boilTime_min ) { this->lineEdit_boilTime ->setQuantity (m_editItem->boilTime_min ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::kettleEvaporationPerHour_l ) { this->lineEdit_kettleEvaporationPerHour->setQuantity (m_editItem->kettleEvaporationPerHour_l ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::topUpKettle_l ) { this->lineEdit_topUpKettle ->setQuantity (m_editItem->topUpKettle_l ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::topUpWater_l ) { this->lineEdit_topUpWater ->setQuantity (m_editItem->topUpWater_l ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::kettleTrubChillerLoss_l ) { this->lineEdit_kettleTrubChillerLoss ->setQuantity (m_editItem->kettleTrubChillerLoss_l ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::lauterTunDeadspaceLoss_l ) { this->lineEdit_lauterTunDeadspaceLoss ->setQuantity (m_editItem->lauterTunDeadspaceLoss_l ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::kettleNotes ) { this->textEdit_kettleNotes ->setText (m_editItem->kettleNotes ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::mashTunGrainAbsorption_LKg ) { this->lineEdit_mashTunGrainAbsorption ->setQuantity (m_editItem->mashTunGrainAbsorption_LKg ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::boilingPoint_c ) { this->lineEdit_boilingPoint ->setQuantity (m_editItem->boilingPoint_c ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::hopUtilization_pct ) { this->lineEdit_hopUtilization ->setQuantity (m_editItem->hopUtilization_pct ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::calcBoilVolume ) { this->checkBox_calcBoilVolume ->setChecked (m_editItem->calcBoilVolume ()); if (propName) { return; } }
- // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞
- if (!propName || *propName == PropertyNames::Equipment::hltType ) { this->lineEdit_hltType ->setTextCursor(m_editItem->hltType ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::mashTunType ) { this->lineEdit_mashTunType ->setTextCursor(m_editItem->mashTunType ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::lauterTunType ) { this->lineEdit_lauterTunType ->setTextCursor(m_editItem->lauterTunType ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::kettleType ) { this->lineEdit_kettleType ->setTextCursor(m_editItem->kettleType ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::fermenterType ) { this->lineEdit_fermenterType ->setTextCursor(m_editItem->fermenterType ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::agingVesselType ) { this->lineEdit_agingVesselType ->setTextCursor(m_editItem->agingVesselType ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::packagingVesselType ) { this->lineEdit_packagingVesselType ->setTextCursor(m_editItem->packagingVesselType ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::hltVolume_l ) { this->lineEdit_hltVolume ->setQuantity (m_editItem->hltVolume_l ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::lauterTunVolume_l ) { this->lineEdit_lauterTunVolume ->setQuantity (m_editItem->lauterTunVolume_l ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::agingVesselVolume_l ) { this->lineEdit_agingVesselVolume ->setQuantity (m_editItem->agingVesselVolume_l ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::packagingVesselVolume_l ) { this->lineEdit_packagingVesselVolume ->setQuantity (m_editItem->packagingVesselVolume_l ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::hltLoss_l ) { this->lineEdit_hltLoss ->setQuantity (m_editItem->hltLoss_l ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::mashTunLoss_l ) { this->lineEdit_mashTunLoss ->setQuantity (m_editItem->mashTunLoss_l ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::fermenterLoss_l ) { this->lineEdit_fermenterLoss ->setQuantity (m_editItem->fermenterLoss_l ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::agingVesselLoss_l ) { this->lineEdit_agingVesselLoss ->setQuantity (m_editItem->agingVesselLoss_l ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::packagingVesselLoss_l ) { this->lineEdit_packagingVesselLoss ->setQuantity (m_editItem->packagingVesselLoss_l ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::kettleOutflowPerMinute_l ) { this->lineEdit_kettleOutflowPerMinute ->setQuantity (m_editItem->kettleOutflowPerMinute_l ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::hltWeight_kg ) { this->lineEdit_hltWeight ->setQuantity (m_editItem->hltWeight_kg ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::lauterTunWeight_kg ) { this->lineEdit_lauterTunWeight ->setQuantity (m_editItem->lauterTunWeight_kg ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::kettleWeight_kg ) { this->lineEdit_kettleWeight ->setQuantity (m_editItem->kettleWeight_kg ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::hltSpecificHeat_calGC ) { this->lineEdit_hltSpecificHeat ->setQuantity (m_editItem->hltSpecificHeat_calGC ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::lauterTunSpecificHeat_calGC) { this->lineEdit_lauterTunSpecificHeat ->setQuantity (m_editItem->lauterTunSpecificHeat_calGC()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::kettleSpecificHeat_calGC ) { this->lineEdit_kettleSpecificHeat ->setQuantity (m_editItem->kettleSpecificHeat_calGC ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::hltNotes ) { this->textEdit_hltNotes ->setText (m_editItem->hltNotes ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::mashTunNotes ) { this->textEdit_mashTunNotes ->setText (m_editItem->mashTunNotes ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::lauterTunNotes ) { this->textEdit_lauterTunNotes ->setText (m_editItem->lauterTunNotes ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::fermenterNotes ) { this->textEdit_fermenterNotes ->setText (m_editItem->fermenterNotes ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::agingVesselNotes ) { this->textEdit_agingVesselNotes ->setText (m_editItem->agingVesselNotes ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Equipment::packagingVesselNotes ) { this->textEdit_packagingVesselNotes ->setText (m_editItem->packagingVesselNotes ()); if (propName) { return; } }
+void EquipmentEditor::postReadFieldsFromEditItem([[maybe_unused]] std::optional propName) {
+ // These aren't fields we store in the DB, rather they control which bits of the editor are visible
this->checkBox_showHlt ->setChecked(m_editItem->hltVolume_l () != 0);
this->checkBox_showLauterTun ->setChecked(m_editItem->lauterTunVolume_l () != 0);
this->checkBox_showAgingVessel ->setChecked(m_editItem->agingVesselVolume_l () != 0);
this->checkBox_showPackagingVessel->setChecked(m_editItem->packagingVesselVolume_l() != 0);
+
this->hideOrShowOptionalVessels();
this->checkBox_defaultEquipment->blockSignals(true);
@@ -418,4 +312,4 @@ void EquipmentEditor::updateDefaultEquipment() {
}
// Insert the boiler-plate stuff that we cannot do in EditorBase
-EDITOR_COMMON_CODE(EquipmentEditor)
+EDITOR_COMMON_CODE(Equipment)
diff --git a/src/editors/EquipmentEditor.h b/src/editors/EquipmentEditor.h
index a0a682d8..05d98c46 100644
--- a/src/editors/EquipmentEditor.h
+++ b/src/editors/EquipmentEditor.h
@@ -30,6 +30,7 @@
#include "editors/EditorBase.h"
#include "model/Equipment.h"
+#define EquipmentEditorOptions EditorBaseOptions{ }
/*!
* \class EquipmentEditor
*
@@ -38,10 +39,12 @@
* See comment on EditorBase::connectSignalsAndSlots for why we need to have \c public, not \c private
* inheritance from the Ui base.
*/
-class EquipmentEditor : public QDialog, public Ui::equipmentEditor, public EditorBase {
+class EquipmentEditor : public QDialog,
+ public Ui::equipmentEditor,
+ public EditorBase {
Q_OBJECT
- EDITOR_COMMON_DECL(Equipment)
+ EDITOR_COMMON_DECL(Equipment, EquipmentEditorOptions)
public slots:
void hideOrShowOptionalVessels();
@@ -53,8 +56,8 @@ public slots:
bool validateBeforeSave();
private:
+ void postReadFieldsFromEditItem(std::optional propName);
double calcBatchSize();
-
};
#endif
diff --git a/src/editors/FermentableEditor.cpp b/src/editors/FermentableEditor.cpp
index 28c90489..6965bb0c 100644
--- a/src/editors/FermentableEditor.cpp
+++ b/src/editors/FermentableEditor.cpp
@@ -24,166 +24,63 @@
#include
#include
-#include "BtHorizontalTabs.h"
#include "database/ObjectStoreWrapper.h"
#include "measurement/Unit.h"
// TODO: Need a separate editor for inventory
-FermentableEditor::FermentableEditor(QWidget* parent) :
+FermentableEditor::FermentableEditor(QWidget* parent, QString const editorName) :
QDialog(parent),
- EditorBase() {
+ EditorBase(editorName) {
setupUi(this);
-
- this->tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs);
-
- SMART_FIELD_INIT(FermentableEditor, label_name , lineEdit_name , Fermentable, PropertyNames::NamedEntity::name );
- SMART_FIELD_INIT(FermentableEditor, label_color , lineEdit_color , Fermentable, PropertyNames::Fermentable::color_srm , 0);
- SMART_FIELD_INIT(FermentableEditor, label_diastaticPower, lineEdit_diastaticPower, Fermentable, PropertyNames::Fermentable::diastaticPower_lintner );
- SMART_FIELD_INIT(FermentableEditor, label_coarseFineDiff, lineEdit_coarseFineDiff, Fermentable, PropertyNames::Fermentable::coarseFineDiff_pct , 0);
- SMART_FIELD_INIT(FermentableEditor, label_ibuGalPerLb , lineEdit_ibuGalPerLb , Fermentable, PropertyNames::Fermentable::ibuGalPerLb , 0);
- SMART_FIELD_INIT(FermentableEditor, label_maxInBatch , lineEdit_maxInBatch , Fermentable, PropertyNames::Fermentable::maxInBatch_pct , 0);
- SMART_FIELD_INIT(FermentableEditor, label_moisture , lineEdit_moisture , Fermentable, PropertyNames::Fermentable::moisture_pct , 0);
- SMART_FIELD_INIT(FermentableEditor, label_protein , lineEdit_protein , Fermentable, PropertyNames::Fermentable::protein_pct , 0);
- SMART_FIELD_INIT(FermentableEditor, label_inventory , lineEdit_inventory , Fermentable, PropertyNames::Ingredient::totalInventory , 1);
- SMART_FIELD_INIT(FermentableEditor, label_origin , lineEdit_origin , Fermentable, PropertyNames::Fermentable::origin );
- SMART_FIELD_INIT(FermentableEditor, label_supplier , lineEdit_supplier , Fermentable, PropertyNames::Fermentable::supplier );
-
- BT_COMBO_BOX_INIT(FermentableEditor, comboBox_type , Fermentable, type );
-
- // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞
- BT_COMBO_BOX_INIT(FermentableEditor, comboBox_grainGroup, Fermentable, grainGroup);
-
- SMART_FIELD_INIT(FermentableEditor, label_producer , lineEdit_producer , Fermentable, PropertyNames::Fermentable::producer );
- SMART_FIELD_INIT(FermentableEditor, label_productId , lineEdit_productId , Fermentable, PropertyNames::Fermentable::productId );
- SMART_FIELD_INIT(FermentableEditor, label_fineGrindYield_pct , lineEdit_fineGrindYield_pct , Fermentable, PropertyNames::Fermentable::fineGrindYield_pct , 1);
- SMART_FIELD_INIT(FermentableEditor, label_coarseGrindYield_pct , lineEdit_coarseGrindYield_pct , Fermentable, PropertyNames::Fermentable::coarseGrindYield_pct , 1);
- SMART_FIELD_INIT(FermentableEditor, label_potentialYield_sg , lineEdit_potentialYield_sg , Fermentable, PropertyNames::Fermentable::potentialYield_sg );
- SMART_FIELD_INIT(FermentableEditor, label_alphaAmylase_dextUnits, lineEdit_alphaAmylase_dextUnits, Fermentable, PropertyNames::Fermentable::alphaAmylase_dextUnits );
- SMART_FIELD_INIT(FermentableEditor, label_kolbachIndex_pct , lineEdit_kolbachIndex_pct , Fermentable, PropertyNames::Fermentable::kolbachIndex_pct , 1);
- SMART_FIELD_INIT(FermentableEditor, label_hardnessPrpGlassy_pct , lineEdit_hardnessPrpGlassy_pct , Fermentable, PropertyNames::Fermentable::hardnessPrpGlassy_pct , 1);
- SMART_FIELD_INIT(FermentableEditor, label_hardnessPrpHalf_pct , lineEdit_hardnessPrpHalf_pct , Fermentable, PropertyNames::Fermentable::hardnessPrpHalf_pct , 1);
- SMART_FIELD_INIT(FermentableEditor, label_hardnessPrpMealy_pct , lineEdit_hardnessPrpMealy_pct , Fermentable, PropertyNames::Fermentable::hardnessPrpMealy_pct , 1);
- SMART_FIELD_INIT(FermentableEditor, label_kernelSizePrpPlump_pct, lineEdit_kernelSizePrpPlump_pct, Fermentable, PropertyNames::Fermentable::kernelSizePrpPlump_pct, 1);
- SMART_FIELD_INIT(FermentableEditor, label_kernelSizePrpThin_pct , lineEdit_kernelSizePrpThin_pct , Fermentable, PropertyNames::Fermentable::kernelSizePrpThin_pct , 1);
- SMART_FIELD_INIT(FermentableEditor, label_friability_pct , lineEdit_friability_pct , Fermentable, PropertyNames::Fermentable::friability_pct , 1);
- SMART_FIELD_INIT(FermentableEditor, label_di_ph , lineEdit_di_ph , Fermentable, PropertyNames::Fermentable::di_ph , 1);
- SMART_FIELD_INIT(FermentableEditor, label_viscosity_cP , lineEdit_viscosity_cP , Fermentable, PropertyNames::Fermentable::viscosity_cP );
- SMART_FIELD_INIT(FermentableEditor, label_dmsP , lineEdit_dmsP , Fermentable, PropertyNames::Fermentable::dmsP_ppm , 1);
- SMART_FIELD_INIT(FermentableEditor, label_fan , lineEdit_fan , Fermentable, PropertyNames::Fermentable::fan_ppm , 1);
- SMART_FIELD_INIT(FermentableEditor, label_fermentability_pct , lineEdit_fermentability_pct , Fermentable, PropertyNames::Fermentable::fermentability_pct , 1);
- SMART_FIELD_INIT(FermentableEditor, label_betaGlucan , lineEdit_betaGlucan , Fermentable, PropertyNames::Fermentable::betaGlucan_ppm , 1);
-
-/// SMART_CHECK_BOX_INIT(FermentableEditor, checkBox_amountIsWeight , label_amountIsWeight , lineEdit_inventory , Fermentable, amountIsWeight );
-
- BT_COMBO_BOX_INIT_COPQ(FermentableEditor, comboBox_amountType, Fermentable, PropertyNames::Ingredient::totalInventory, lineEdit_inventory);
-
- this->connectSignalsAndSlots();
+ this->postSetupUiInit({
+ //
+ // Write inventory late to make sure we've the row in the inventory table (because total inventory amount isn't
+ // really an attribute of the Fermentable).
+ //
+ // Note that we do not need to store the value of comboBox_amountType. It merely controls the available unit for
+ // lineEdit_inventory
+ //
+ EDITOR_FIELD_NORM(Fermentable, label_name , lineEdit_name , NamedEntity::name ),
+ EDITOR_FIELD_NORM(Fermentable, tab_notes , textEdit_notes , Fermentable::notes ),
+ EDITOR_FIELD_NORM(Fermentable, label_color , lineEdit_color , Fermentable::color_srm , 0),
+ EDITOR_FIELD_NORM(Fermentable, label_diastaticPower , lineEdit_diastaticPower , Fermentable::diastaticPower_lintner ),
+ EDITOR_FIELD_NORM(Fermentable, label_coarseFineDiff , lineEdit_coarseFineDiff , Fermentable::coarseFineDiff_pct , 0),
+ EDITOR_FIELD_NORM(Fermentable, label_ibuGalPerLb , lineEdit_ibuGalPerLb , Fermentable::ibuGalPerLb , 0),
+ EDITOR_FIELD_NORM(Fermentable, label_maxInBatch , lineEdit_maxInBatch , Fermentable::maxInBatch_pct , 0),
+ EDITOR_FIELD_NORM(Fermentable, label_moisture , lineEdit_moisture , Fermentable::moisture_pct , 0),
+ EDITOR_FIELD_NORM(Fermentable, label_protein , lineEdit_protein , Fermentable::protein_pct , 0),
+ EDITOR_FIELD_NORM(Fermentable, label_inventory , lineEdit_inventory , Ingredient::totalInventory , 1, WhenToWriteField::Late),
+ EDITOR_FIELD_COPQ(Fermentable, label_amountType , comboBox_amountType , Ingredient::totalInventory, lineEdit_inventory, WhenToWriteField::Never),
+ EDITOR_FIELD_NORM(Fermentable, label_origin , lineEdit_origin , Fermentable::origin ),
+ EDITOR_FIELD_NORM(Fermentable, label_supplier , lineEdit_supplier , Fermentable::supplier ),
+ EDITOR_FIELD_ENUM(Fermentable, label_fermentableType , comboBox_type , Fermentable::type ),
+ // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞
+ EDITOR_FIELD_ENUM(Fermentable, label_grainGroup , comboBox_grainGroup , Fermentable::grainGroup ),
+ EDITOR_FIELD_NORM(Fermentable, label_producer , lineEdit_producer , Fermentable::producer ),
+ EDITOR_FIELD_NORM(Fermentable, label_productId , lineEdit_productId , Fermentable::productId ),
+ EDITOR_FIELD_NORM(Fermentable, label_fineGrindYield_pct , lineEdit_fineGrindYield_pct , Fermentable::fineGrindYield_pct , 1),
+ EDITOR_FIELD_NORM(Fermentable, label_coarseGrindYield_pct , lineEdit_coarseGrindYield_pct , Fermentable::coarseGrindYield_pct , 1),
+ EDITOR_FIELD_NORM(Fermentable, label_potentialYield_sg , lineEdit_potentialYield_sg , Fermentable::potentialYield_sg ),
+ EDITOR_FIELD_NORM(Fermentable, label_alphaAmylase_dextUnits, lineEdit_alphaAmylase_dextUnits, Fermentable::alphaAmylase_dextUnits ),
+ EDITOR_FIELD_NORM(Fermentable, label_kolbachIndex_pct , lineEdit_kolbachIndex_pct , Fermentable::kolbachIndex_pct , 1),
+ EDITOR_FIELD_NORM(Fermentable, label_hardnessPrpGlassy_pct , lineEdit_hardnessPrpGlassy_pct , Fermentable::hardnessPrpGlassy_pct , 1),
+ EDITOR_FIELD_NORM(Fermentable, label_hardnessPrpHalf_pct , lineEdit_hardnessPrpHalf_pct , Fermentable::hardnessPrpHalf_pct , 1),
+ EDITOR_FIELD_NORM(Fermentable, label_hardnessPrpMealy_pct , lineEdit_hardnessPrpMealy_pct , Fermentable::hardnessPrpMealy_pct , 1),
+ EDITOR_FIELD_NORM(Fermentable, label_kernelSizePrpPlump_pct, lineEdit_kernelSizePrpPlump_pct, Fermentable::kernelSizePrpPlump_pct, 1),
+ EDITOR_FIELD_NORM(Fermentable, label_kernelSizePrpThin_pct , lineEdit_kernelSizePrpThin_pct , Fermentable::kernelSizePrpThin_pct , 1),
+ EDITOR_FIELD_NORM(Fermentable, label_friability_pct , lineEdit_friability_pct , Fermentable::friability_pct , 1),
+ EDITOR_FIELD_NORM(Fermentable, label_di_ph , lineEdit_di_ph , Fermentable::di_ph , 1),
+ EDITOR_FIELD_NORM(Fermentable, label_viscosity_cP , lineEdit_viscosity_cP , Fermentable::viscosity_cP ),
+ EDITOR_FIELD_NORM(Fermentable, label_dmsP , lineEdit_dmsP , Fermentable::dmsP_ppm , 1),
+ EDITOR_FIELD_NORM(Fermentable, label_fan , lineEdit_fan , Fermentable::fan_ppm , 1),
+ EDITOR_FIELD_NORM(Fermentable, label_fermentability_pct , lineEdit_fermentability_pct , Fermentable::fermentability_pct , 1),
+ EDITOR_FIELD_NORM(Fermentable, label_betaGlucan , lineEdit_betaGlucan , Fermentable::betaGlucan_ppm , 1),
+ });
return;
}
FermentableEditor::~FermentableEditor() = default;
-void FermentableEditor::writeFieldsToEditItem() {
- this->m_editItem->setType(this->comboBox_type ->getNonOptValue());
-
- this->m_editItem->setName (this->lineEdit_name ->text ());
- this->m_editItem->setColor_srm (this->lineEdit_color ->getNonOptCanonicalQty());
- this->m_editItem->setOrigin (this->lineEdit_origin ->text ());
- this->m_editItem->setSupplier (this->lineEdit_supplier ->text ());
- this->m_editItem->setCoarseFineDiff_pct (this->lineEdit_coarseFineDiff->getOptValue ());
- this->m_editItem->setMoisture_pct (this->lineEdit_moisture ->getOptValue ());
- this->m_editItem->setDiastaticPower_lintner(this->lineEdit_diastaticPower->getOptCanonicalQty ());
- this->m_editItem->setProtein_pct (this->lineEdit_protein ->getOptValue ());
- // See below for call to setTotalInventory, which needs to be done "late"
- this->m_editItem->setMaxInBatch_pct (this->lineEdit_maxInBatch ->getOptValue ());
- this->m_editItem->setRecommendMash (this->checkBox_recommendMash ->checkState() == Qt::Checked);
- this->m_editItem->setIbuGalPerLb (this->lineEdit_ibuGalPerLb ->getOptValue()); // .:TBD:. No metric measure?
- this->m_editItem->setNotes (this->textEdit_notes ->toPlainText ());
- // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞
- this->m_editItem->setGrainGroup (this->comboBox_grainGroup ->getOptValue());
- this->m_editItem->setProducer (this->lineEdit_producer ->text ());
- this->m_editItem->setProductId (this->lineEdit_productId ->text ());
- this->m_editItem->setFineGrindYield_pct (this->lineEdit_fineGrindYield_pct ->getOptValue());
- this->m_editItem->setCoarseGrindYield_pct (this->lineEdit_coarseGrindYield_pct ->getOptValue());
- this->m_editItem->setPotentialYield_sg (this->lineEdit_potentialYield_sg ->getOptValue());
- this->m_editItem->setAlphaAmylase_dextUnits (this->lineEdit_alphaAmylase_dextUnits ->getOptValue());
- this->m_editItem->setKolbachIndex_pct (this->lineEdit_kolbachIndex_pct ->getOptValue());
- this->m_editItem->setHardnessPrpGlassy_pct (this->lineEdit_hardnessPrpGlassy_pct ->getOptValue());
- this->m_editItem->setHardnessPrpHalf_pct (this->lineEdit_hardnessPrpHalf_pct ->getOptValue());
- this->m_editItem->setHardnessPrpMealy_pct (this->lineEdit_hardnessPrpMealy_pct ->getOptValue());
- this->m_editItem->setKernelSizePrpPlump_pct (this->lineEdit_kernelSizePrpPlump_pct ->getOptValue());
- this->m_editItem->setKernelSizePrpThin_pct (this->lineEdit_kernelSizePrpThin_pct ->getOptValue());
- this->m_editItem->setFriability_pct (this->lineEdit_friability_pct ->getOptValue());
- this->m_editItem->setDi_ph (this->lineEdit_di_ph ->getOptValue());
- this->m_editItem->setViscosity_cP (this->lineEdit_viscosity_cP ->getOptValue());
- this->m_editItem->setDmsP_ppm (this->lineEdit_dmsP ->getOptCanonicalQty());
- this->m_editItem->setFan_ppm (this->lineEdit_fan ->getOptCanonicalQty());
- this->m_editItem->setFermentability_pct (this->lineEdit_fermentability_pct ->getOptValue());
- this->m_editItem->setBetaGlucan_ppm (this->lineEdit_betaGlucan ->getOptCanonicalQty());
-
- return;
-}
-
-void FermentableEditor::writeLateFieldsToEditItem() {
- //
- // Do this late to make sure we've the row in the inventory table (because total inventory amount isn't really an
- // attribute of the Fermentable).
- //
- // Note that we do not need to store the value of comboBox_amountType. It merely controls the available unit for
- // lineEdit_inventory
- //
- // Note that, if the inventory field is blank, we'll treat that as meaning "don't change the inventory"
- //
- if (!this->lineEdit_inventory->isEmptyOrBlank()) {
- this->m_editItem->setTotalInventory(lineEdit_inventory->getNonOptCanonicalAmt());
- }
- return;
-}
-
-void FermentableEditor::readFieldsFromEditItem(std::optional propName) {
- if (!propName || *propName == PropertyNames::NamedEntity::name ) { this->lineEdit_name ->setTextCursor(m_editItem->name ()); // Continues to next line
- this->tabWidget_editor->setTabText(0, m_editItem->name()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::type ) { this->comboBox_type ->setValue (m_editItem->type ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::color_srm ) { this->lineEdit_color ->setQuantity (m_editItem->color_srm ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::origin ) { this->lineEdit_origin ->setTextCursor(m_editItem->origin ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::supplier ) { this->lineEdit_supplier ->setTextCursor(m_editItem->supplier ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::coarseFineDiff_pct ) { this->lineEdit_coarseFineDiff->setQuantity (m_editItem->coarseFineDiff_pct ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::moisture_pct ) { this->lineEdit_moisture ->setQuantity (m_editItem->moisture_pct ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::diastaticPower_lintner) { this->lineEdit_diastaticPower->setQuantity (m_editItem->diastaticPower_lintner()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::protein_pct ) { this->lineEdit_protein ->setQuantity (m_editItem->protein_pct ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Ingredient::totalInventory ) { this->lineEdit_inventory ->setAmount (m_editItem->totalInventory ());
- this->comboBox_amountType ->autoSetFromControlledField();
- if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::maxInBatch_pct ) { this->lineEdit_maxInBatch ->setQuantity (m_editItem->maxInBatch_pct ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::recommendMash ) { this->checkBox_recommendMash ->setCheckState(m_editItem->recommendMash() ? Qt::Checked : Qt::Unchecked); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::ibuGalPerLb ) { this->lineEdit_ibuGalPerLb ->setQuantity (m_editItem->ibuGalPerLb ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::notes ) { this->textEdit_notes ->setPlainText (m_editItem->notes ()); if (propName) { return; } }
- // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞
- if (!propName || *propName == PropertyNames::Fermentable::grainGroup ) { this->comboBox_grainGroup ->setValue (m_editItem->grainGroup ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::producer ) { this->lineEdit_producer ->setTextCursor(m_editItem->producer ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::productId ) { this->lineEdit_productId ->setTextCursor(m_editItem->productId ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::fineGrindYield_pct ) { this->lineEdit_fineGrindYield_pct ->setQuantity (m_editItem->fineGrindYield_pct ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::coarseGrindYield_pct ) { this->lineEdit_coarseGrindYield_pct ->setQuantity (m_editItem->coarseGrindYield_pct ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::potentialYield_sg ) { this->lineEdit_potentialYield_sg ->setQuantity (m_editItem->potentialYield_sg ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::alphaAmylase_dextUnits ) { this->lineEdit_alphaAmylase_dextUnits ->setQuantity (m_editItem->alphaAmylase_dextUnits ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::kolbachIndex_pct ) { this->lineEdit_kolbachIndex_pct ->setQuantity (m_editItem->kolbachIndex_pct ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::hardnessPrpGlassy_pct ) { this->lineEdit_hardnessPrpGlassy_pct ->setQuantity (m_editItem->hardnessPrpGlassy_pct ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::hardnessPrpHalf_pct ) { this->lineEdit_hardnessPrpHalf_pct ->setQuantity (m_editItem->hardnessPrpHalf_pct ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::hardnessPrpMealy_pct ) { this->lineEdit_hardnessPrpMealy_pct ->setQuantity (m_editItem->hardnessPrpMealy_pct ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::kernelSizePrpPlump_pct ) { this->lineEdit_kernelSizePrpPlump_pct ->setQuantity (m_editItem->kernelSizePrpPlump_pct ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::kernelSizePrpThin_pct ) { this->lineEdit_kernelSizePrpThin_pct ->setQuantity (m_editItem->kernelSizePrpThin_pct ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::friability_pct ) { this->lineEdit_friability_pct ->setQuantity (m_editItem->friability_pct ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::di_ph ) { this->lineEdit_di_ph ->setQuantity (m_editItem->di_ph ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::viscosity_cP ) { this->lineEdit_viscosity_cP ->setQuantity (m_editItem->viscosity_cP ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::dmsP_ppm ) { this->lineEdit_dmsP ->setQuantity (m_editItem->dmsP_ppm ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::fan_ppm ) { this->lineEdit_fan ->setQuantity (m_editItem->fan_ppm ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::fermentability_pct ) { this->lineEdit_fermentability_pct ->setQuantity (m_editItem->fermentability_pct ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::Fermentable::betaGlucan_ppm ) { this->lineEdit_betaGlucan ->setQuantity (m_editItem->betaGlucan_ppm ()); if (propName) { return; } }
-
- this->label_id_value->setText(QString::number(m_editItem->key()));
- return;
-}
-
// Insert the boiler-plate stuff that we cannot do in EditorBase
-EDITOR_COMMON_CODE(FermentableEditor)
+EDITOR_COMMON_CODE(Fermentable)
diff --git a/src/editors/FermentableEditor.h b/src/editors/FermentableEditor.h
index b5a166f5..2c123ac3 100644
--- a/src/editors/FermentableEditor.h
+++ b/src/editors/FermentableEditor.h
@@ -30,6 +30,7 @@
#include "editors/EditorBase.h"
#include "model/Fermentable.h"
+#define FermentableEditorOptions EditorBaseOptions{ .nameTab = true, .idDisplay = true }
/*!
* \class FermentableEditor
*
@@ -38,10 +39,12 @@
* See comment on EditorBase::connectSignalsAndSlots for why we need to have \c public, not \c private
* inheritance from the Ui base.
*/
-class FermentableEditor : public QDialog, public Ui::fermentableEditor, public EditorBase {
+class FermentableEditor : public QDialog,
+ public Ui::fermentableEditor,
+ public EditorBase {
Q_OBJECT
- EDITOR_COMMON_DECL(Fermentable)
+ EDITOR_COMMON_DECL(Fermentable, FermentableEditorOptions)
};
#endif
diff --git a/src/editors/FermentationEditor.cpp b/src/editors/FermentationEditor.cpp
index c96f9b77..d9d35731 100644
--- a/src/editors/FermentationEditor.cpp
+++ b/src/editors/FermentationEditor.cpp
@@ -22,141 +22,19 @@
#include "model/Fermentation.h"
#include "model/Recipe.h"
-FermentationEditor::FermentationEditor(QWidget* parent) :
+FermentationEditor::FermentationEditor(QWidget* parent, QString const editorName) :
QDialog(parent),
- EditorWithRecipeBase() {
+ EditorBase(editorName) {
this->setupUi(this);
- this->postSetupUiInit(
- {
- EDITOR_FIELD(Fermentation, label_name , lineEdit_name , PropertyNames::NamedEntity::name ),
- EDITOR_FIELD(Fermentation, label_description, textEdit_description, PropertyNames::Fermentation::description ),
- EDITOR_FIELD(Fermentation, label_notes , textEdit_notes , PropertyNames::Fermentation::notes )
- }
- );
-
-/// // NB: label_description / textEdit_description don't need initialisation here as neither is a smart field
-/// // NB: label_notes / textEdit_notes don't need initialisation here as neither is a smart field
-/// SMART_FIELD_INIT(FermentationEditor, label_name , lineEdit_name , Fermentation, PropertyNames::NamedEntity::name );
-///
-/// connect(this, &QDialog::accepted, this, &FermentationEditor::saveAndClose);
-/// connect(this, &QDialog::rejected, this, &FermentationEditor::closeEditor );
+ this->postSetupUiInit({
+ EDITOR_FIELD_NORM(Fermentation, label_name , lineEdit_name , NamedEntity::name ),
+ EDITOR_FIELD_NORM(Fermentation, label_description, textEdit_description, Fermentation::description),
+ EDITOR_FIELD_NORM(Fermentation, label_notes , textEdit_notes , Fermentation::notes ),
+ });
return;
}
FermentationEditor::~FermentationEditor() = default;
-void FermentationEditor::writeFieldsToEditItem() {
- return;
-}
-
-void FermentationEditor::writeLateFieldsToEditItem() {
- return;
-}
-
-void FermentationEditor::readFieldsFromEditItem([[maybe_unused]] std::optional propName) {
- return;
-}
-
// Insert the boilerplate stuff that we cannot do in EditorWithRecipeBase
-EDITOR_WITH_RECIPE_COMMON_CODE(FermentationEditor)
-
-///void FermentationEditor::showEditor() {
-/// showChanges();
-/// setVisible(true);
-/// return;
-///}
-///
-///void FermentationEditor::closeEditor() {
-/// setVisible(false);
-/// return;
-///}
-///
-///void FermentationEditor::saveAndClose() {
-/// bool isNew = false;
-///
-/// if (!this->m_fermentationObs) {
-/// this->m_fermentationObs = std::make_shared(lineEdit_name->text());
-/// isNew = true;
-/// }
-/// qDebug() << Q_FUNC_INFO << "Saving" << (isNew ? "new" : "existing") << "fermentation (#" << this->m_fermentationObs->key() << ")";
-///
-/// this->m_fermentationObs->setName (this->lineEdit_name ->text ());
-/// this->m_fermentationObs->setDescription (this->textEdit_description->toPlainText ());
-/// this->m_fermentationObs->setNotes (this->textEdit_notes ->toPlainText ());
-///
-/// if (isNew) {
-/// ObjectStoreWrapper::insert(this->m_fermentationObs);
-/// this->m_rec->setFermentation(this->m_fermentationObs);
-/// }
-///
-/// return;
-///}
-///
-///void FermentationEditor::setFermentation(std::shared_ptr fermentation) {
-/// if (this->m_fermentationObs) {
-/// disconnect(this->m_fermentationObs.get(), nullptr, this, nullptr);
-/// }
-///
-/// this->m_fermentationObs = fermentation;
-/// if (this->m_fermentationObs) {
-/// connect(this->m_fermentationObs.get(), &NamedEntity::changed, this, &FermentationEditor::changed);
-/// showChanges();
-/// }
-/// return;
-///}
-///
-///void FermentationEditor::setRecipe(Recipe * recipe) {
-/// if (!recipe) {
-/// return;
-/// }
-///
-/// this->m_rec = recipe;
-///
-/// return;
-///}
-///
-///void FermentationEditor::changed(QMetaProperty prop, QVariant /*val*/) {
-/// if (!this->m_fermentationObs) {
-/// return;
-/// }
-///
-///
-/// if (sender() == this->m_fermentationObs.get()) {
-/// this->showChanges(&prop);
-/// }
-///
-/// if (sender() == this->m_rec) {
-/// this->showChanges();
-/// }
-/// return;
-///}
-///
-///void FermentationEditor::showChanges(QMetaProperty* prop) {
-/// if (!this->m_fermentationObs) {
-/// this->clear();
-/// return;
-/// }
-///
-/// QString propName;
-/// bool updateAll = false;
-/// if (prop == nullptr) {
-/// updateAll = true;
-/// } else {
-/// propName = prop->name();
-/// }
-/// qDebug() << Q_FUNC_INFO << "Updating" << (updateAll ? "all" : "property") << propName;
-///
-/// if (updateAll || propName == PropertyNames::NamedEntity::name ) {this->lineEdit_name ->setText (m_fermentationObs->name ()); if (!updateAll) { return; } }
-/// if (updateAll || propName == PropertyNames::Fermentation::description) {this->textEdit_description->setPlainText(m_fermentationObs->description()); if (!updateAll) { return; } }
-/// if (updateAll || propName == PropertyNames::Fermentation::notes ) {this->textEdit_notes ->setPlainText(m_fermentationObs->notes ()); if (!updateAll) { return; } }
-/// return;
-///}
-///
-///void FermentationEditor::clear() {
-/// this->lineEdit_name ->setText ("");
-/// this->textEdit_description ->setText ("");
-/// this->lineEdit_preFermentationSize->setText ("");
-/// this->lineEdit_fermentationTime ->setText ("");
-/// this->textEdit_notes ->setPlainText("");
-/// return;
-///}
+EDITOR_COMMON_CODE(Fermentation)
diff --git a/src/editors/FermentationEditor.h b/src/editors/FermentationEditor.h
index 5338f515..6a66414b 100644
--- a/src/editors/FermentationEditor.h
+++ b/src/editors/FermentationEditor.h
@@ -23,9 +23,10 @@
#include "ui_fermentationEditor.h"
-#include "editors/EditorWithRecipeBase.h"
+#include "editors/EditorBase.h"
#include "model/Fermentation.h"
+#define FermentationEditorOptions EditorBaseOptions{ .recipe = true }
/*!
* \class FermentationEditor
*
@@ -35,32 +36,10 @@
*/
class FermentationEditor : public QDialog,
public Ui::fermentationEditor,
- public EditorWithRecipeBase {
+ public EditorBase {
Q_OBJECT
- EDITOR_WITH_RECIPE_COMMON_DECL(Fermentation)
+ EDITOR_COMMON_DECL(Fermentation, FermentationEditorOptions)
};
-///class FermentationEditor : public QDialog, public Ui::fermentationEditor {
-/// Q_OBJECT
-///public:
-/// FermentationEditor(QWidget * parent = nullptr);
-/// ~FermentationEditor();
-///
-///public slots:
-/// void showEditor();
-/// void closeEditor();
-/// void saveAndClose();
-/// //! Set the fermentation we wish to view/edit.
-/// void setFermentation(std::shared_ptr fermentation);
-/// void setRecipe(Recipe* r);
-///
-/// void changed(QMetaProperty,QVariant);
-///private:
-/// void showChanges(QMetaProperty* prop = nullptr);
-/// void clear();
-/// Recipe* m_rec;
-/// std::shared_ptr m_fermentationObs;
-///};
-
#endif
diff --git a/src/editors/FermentationStepEditor.cpp b/src/editors/FermentationStepEditor.cpp
index 14393f3c..b9e18d95 100644
--- a/src/editors/FermentationStepEditor.cpp
+++ b/src/editors/FermentationStepEditor.cpp
@@ -18,74 +18,31 @@
#include "MainWindow.h"
#include "measurement/Unit.h"
-FermentationStepEditor::FermentationStepEditor(QWidget* parent) :
+FermentationStepEditor::FermentationStepEditor(QWidget* parent, QString const editorName) :
QDialog{parent},
- EditorBase() {
+ EditorBase(editorName) {
this->setupUi(this);
-
- // NB: Although FermentationStep inherits (via StepExtended) from Step, the rampTime_mins field is not used and
- // should not be stored in the DB or serialised. See comment in model/Step.h. There should therefore not be
- // any label_rampTime or lineEdit_rampTime fields in the .ui file!
- //
- // NB: label_description / textEdit_description don't need initialisation here as neither is a smart field
- SMART_FIELD_INIT(FermentationStepEditor, label_name , lineEdit_name , FermentationStep, PropertyNames:: NamedEntity::name );
- SMART_FIELD_INIT(FermentationStepEditor, label_startTemp , lineEdit_startTemp , FermentationStep, PropertyNames:: Step::startTemp_c , 1);
- SMART_FIELD_INIT(FermentationStepEditor, label_stepTime , lineEdit_stepTime , FermentationStep, PropertyNames:: Step::stepTime_mins , 0);
- SMART_FIELD_INIT(FermentationStepEditor, label_endTemp , lineEdit_endTemp , FermentationStep, PropertyNames:: Step::endTemp_c , 1);
- SMART_FIELD_INIT(FermentationStepEditor, label_startAcidity , lineEdit_startAcidity , FermentationStep, PropertyNames:: Step::startAcidity_pH, 1);
- SMART_FIELD_INIT(FermentationStepEditor, label_endAcidity , lineEdit_endAcidity , FermentationStep, PropertyNames:: Step::endAcidity_pH , 1);
- SMART_FIELD_INIT(FermentationStepEditor, label_startGravity , lineEdit_startGravity , FermentationStep, PropertyNames:: StepExtended::startGravity_sg, 3);
- SMART_FIELD_INIT(FermentationStepEditor, label_endGravity , lineEdit_endGravity , FermentationStep, PropertyNames:: StepExtended::endGravity_sg , 3);
- SMART_FIELD_INIT(FermentationStepEditor, label_vessel , lineEdit_vessel , FermentationStep, PropertyNames::FermentationStep::vessel );
- BT_BOOL_COMBO_BOX_INIT(FermentationStepEditor, boolCombo_freeRise, FermentationStep, freeRise);
-
- this->connectSignalsAndSlots();
+ this->postSetupUiInit({
+ // NB: Although FermentationStep inherits (via StepExtended) from Step, the rampTime_mins field is not used and
+ // should not be stored in the DB or serialised. See comment in model/Step.h. There should therefore not be
+ // any label_rampTime or lineEdit_rampTime fields in the .ui file!
+ EDITOR_FIELD_NORM(FermentationStep, label_name , lineEdit_name , NamedEntity::name ),
+ EDITOR_FIELD_NORM(FermentationStep, label_description , textEdit_description , Step::description ),
+ EDITOR_FIELD_NORM(FermentationStep, label_startTemp , lineEdit_startTemp , Step::startTemp_c , 1),
+ EDITOR_FIELD_NORM(FermentationStep, label_stepTime , lineEdit_stepTime , Step::stepTime_mins , 0),
+ EDITOR_FIELD_NORM(FermentationStep, label_endTemp , lineEdit_endTemp , Step::endTemp_c , 1),
+ EDITOR_FIELD_NORM(FermentationStep, label_startAcidity, lineEdit_startAcidity , Step::startAcidity_pH, 1),
+ EDITOR_FIELD_NORM(FermentationStep, label_endAcidity , lineEdit_endAcidity , Step::endAcidity_pH , 1),
+ EDITOR_FIELD_NORM(FermentationStep, label_startGravity, lineEdit_startGravity , StepExtended::startGravity_sg, 3),
+ EDITOR_FIELD_NORM(FermentationStep, label_endGravity , lineEdit_endGravity , StepExtended::endGravity_sg , 3),
+ EDITOR_FIELD_NORM(FermentationStep, label_vessel , lineEdit_vessel , FermentationStep::vessel ),
+ EDITOR_FIELD_NORM(FermentationStep, label_freeRise , boolCombo_freeRise , FermentationStep::freeRise ),
+ });
return;
}
FermentationStepEditor::~FermentationStepEditor() = default;
-void FermentationStepEditor::readFieldsFromEditItem(std::optional propName) {
- // NB: Although FermentationStep inherits (via StepExtended) from Step, the rampTime_mins field is not used and
- // should not be stored in the DB or serialised. See comment in model/Step.h.
- if (!propName || *propName == PropertyNames:: NamedEntity::name ) { this->lineEdit_name ->setTextCursor(m_editItem->name ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames:: Step::description ) { this->textEdit_description ->setPlainText (m_editItem->description ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames:: Step::startTemp_c ) { this->lineEdit_startTemp ->setQuantity (m_editItem->startTemp_c ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames:: Step::stepTime_mins ) { this->lineEdit_stepTime ->setQuantity (m_editItem->stepTime_mins ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames:: Step::endTemp_c ) { this->lineEdit_endTemp ->setQuantity (m_editItem->endTemp_c ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames:: Step::startAcidity_pH) { this->lineEdit_startAcidity->setQuantity (m_editItem->startAcidity_pH()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames:: Step::endAcidity_pH ) { this->lineEdit_endAcidity ->setQuantity (m_editItem->endAcidity_pH ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::StepExtended::startGravity_sg) { this->lineEdit_startGravity->setQuantity (m_editItem->startGravity_sg()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::StepExtended::endGravity_sg ) { this->lineEdit_endGravity ->setQuantity (m_editItem->endGravity_sg ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::FermentationStep::vessel ) { this->lineEdit_vessel ->setTextCursor(m_editItem->vessel ()); if (propName) { return; } }
- if (!propName || *propName == PropertyNames::FermentationStep::freeRise ) { this->boolCombo_freeRise ->setValue (m_editItem->freeRise ()); if (propName) { return; } }
-
- return;
-}
-
-void FermentationStepEditor::writeFieldsToEditItem() {
- // NB: Although FermentationStep inherits (via StepExtended) from Step, the rampTime_mins field is not used and
- // should not be stored in the DB or serialised. See comment in model/Step.h.
- this->m_editItem->setName (this->lineEdit_name ->text ());
- this->m_editItem->setDescription (this->textEdit_description ->toPlainText ());
- this->m_editItem->setStartTemp_c (this->lineEdit_startTemp ->getOptCanonicalQty());
- this->m_editItem->setStepTime_mins (this->lineEdit_stepTime ->getOptCanonicalQty());
- this->m_editItem->setEndTemp_c (this->lineEdit_endTemp ->getOptCanonicalQty());
- this->m_editItem->setStartAcidity_pH(this->lineEdit_startAcidity->getOptCanonicalQty());
- this->m_editItem->setEndAcidity_pH (this->lineEdit_endAcidity ->getOptCanonicalQty());
- this->m_editItem->setStartGravity_sg(this->lineEdit_startGravity->getOptCanonicalQty());
- this->m_editItem->setEndGravity_sg (this->lineEdit_endGravity ->getOptCanonicalQty());
- this->m_editItem->setVessel (this->lineEdit_vessel ->text ());
- this->m_editItem->setFreeRise (this->boolCombo_freeRise ->getOptBoolValue ());
-
- return;
-}
-
-void FermentationStepEditor::writeLateFieldsToEditItem() {
- // Nothing to do here
- return;
-}
-
// Insert the boilerplate stuff that we cannot do in EditorBase
-EDITOR_COMMON_CODE(FermentationStepEditor)
+EDITOR_COMMON_CODE(FermentationStep)
diff --git a/src/editors/FermentationStepEditor.h b/src/editors/FermentationStepEditor.h
index 73d06f6b..f4755caf 100644
--- a/src/editors/FermentationStepEditor.h
+++ b/src/editors/FermentationStepEditor.h
@@ -24,6 +24,7 @@
#include "editors/EditorBase.h"
#include "model/FermentationStep.h"
+#define FermentationStepEditorOptions EditorBaseOptions{ }
/*!
* \class FermentationStepEditor
*
@@ -31,10 +32,10 @@
*/
class FermentationStepEditor : public QDialog,
public Ui::fermentationStepEditor,
- public EditorBase {
+ public EditorBase {
Q_OBJECT
- EDITOR_COMMON_DECL(FermentationStep)
+ EDITOR_COMMON_DECL(FermentationStep, FermentationStepEditorOptions)
};
diff --git a/src/editors/HopEditor.cpp b/src/editors/HopEditor.cpp
index 3088871c..a66a0437 100644
--- a/src/editors/HopEditor.cpp
+++ b/src/editors/HopEditor.cpp
@@ -31,138 +31,51 @@
// TODO: Need a separate editor for inventory
-HopEditor::HopEditor(QWidget * parent) :
+HopEditor::HopEditor(QWidget * parent, QString const editorName) :
QDialog(parent),
- EditorBase() {
+ EditorBase(editorName) {
setupUi(this);
-
- this->tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs);
-
- SMART_FIELD_INIT(HopEditor, label_name , lineEdit_name , Hop, PropertyNames::NamedEntity::name );
- SMART_FIELD_INIT(HopEditor, label_alpha , lineEdit_alpha , Hop, PropertyNames::Hop::alpha_pct , 1);
- SMART_FIELD_INIT(HopEditor, label_inventory , lineEdit_inventory , Hop, PropertyNames::Ingredient::totalInventory, 1);
- SMART_FIELD_INIT(HopEditor, label_beta , lineEdit_beta , Hop, PropertyNames::Hop::beta_pct , 1);
- SMART_FIELD_INIT(HopEditor, label_HSI , lineEdit_HSI , Hop, PropertyNames::Hop::hsi_pct , 0);
- SMART_FIELD_INIT(HopEditor, label_origin , lineEdit_origin , Hop, PropertyNames::Hop::origin );
- SMART_FIELD_INIT(HopEditor, label_humulene , lineEdit_humulene , Hop, PropertyNames::Hop::humulene_pct , 2);
- SMART_FIELD_INIT(HopEditor, label_caryophyllene , lineEdit_caryophyllene , Hop, PropertyNames::Hop::caryophyllene_pct , 2);
- SMART_FIELD_INIT(HopEditor, label_cohumulone , lineEdit_cohumulone , Hop, PropertyNames::Hop::cohumulone_pct , 2);
- SMART_FIELD_INIT(HopEditor, label_myrcene , lineEdit_myrcene , Hop, PropertyNames::Hop::myrcene_pct , 2);
- // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞
- SMART_FIELD_INIT(HopEditor, label_producer , lineEdit_producer , Hop, PropertyNames::Hop::producer );
- SMART_FIELD_INIT(HopEditor, label_productId , lineEdit_productId , Hop, PropertyNames::Hop::productId );
- SMART_FIELD_INIT(HopEditor, label_year , lineEdit_year , Hop, PropertyNames::Hop::year );
- SMART_FIELD_INIT(HopEditor, label_totalOil_mlPer100g, lineEdit_totalOil_mlPer100g, Hop, PropertyNames::Hop::totalOil_mlPer100g );
- SMART_FIELD_INIT(HopEditor, label_farnesene , lineEdit_farnesene , Hop, PropertyNames::Hop::farnesene_pct , 2);
- SMART_FIELD_INIT(HopEditor, label_geraniol , lineEdit_geraniol , Hop, PropertyNames::Hop::geraniol_pct , 2);
- SMART_FIELD_INIT(HopEditor, label_bPinene , lineEdit_bPinene , Hop, PropertyNames::Hop::bPinene_pct , 2);
- SMART_FIELD_INIT(HopEditor, label_linalool , lineEdit_linalool , Hop, PropertyNames::Hop::linalool_pct , 2);
- SMART_FIELD_INIT(HopEditor, label_limonene , lineEdit_limonene , Hop, PropertyNames::Hop::limonene_pct , 2);
- SMART_FIELD_INIT(HopEditor, label_nerol , lineEdit_nerol , Hop, PropertyNames::Hop::nerol_pct , 2);
- SMART_FIELD_INIT(HopEditor, label_pinene , lineEdit_pinene , Hop, PropertyNames::Hop::pinene_pct , 2);
- SMART_FIELD_INIT(HopEditor, label_polyphenols , lineEdit_polyphenols , Hop, PropertyNames::Hop::polyphenols_pct , 2);
- SMART_FIELD_INIT(HopEditor, label_xanthohumol , lineEdit_xanthohumol , Hop, PropertyNames::Hop::xanthohumol_pct , 2);
-
-/// SMART_CHECK_BOX_INIT(HopEditor, checkBox_amountIsWeight , label_amountIsWeight , lineEdit_inventory , Hop, amountIsWeight );
-
- BT_COMBO_BOX_INIT_COPQ(HopEditor, comboBox_amountType, Hop, PropertyNames::Ingredient::totalInventory, lineEdit_inventory);
-
- BT_COMBO_BOX_INIT(HopEditor, comboBox_hopType, Hop, type);
- BT_COMBO_BOX_INIT(HopEditor, comboBox_hopForm, Hop, form);
-
- this->connectSignalsAndSlots();
+ this->postSetupUiInit({
+ //
+ // Write inventory late to make sure we've the row in the inventory table (because total inventory amount isn't
+ // really an attribute of the Fermentable).
+ //
+ // Note that we do not need to store the value of comboBox_amountType. It merely controls the available unit for
+ // lineEdit_inventory
+ //
+ EDITOR_FIELD_NORM(Hop, label_name , lineEdit_name , NamedEntity::name ),
+ EDITOR_FIELD_NORM(Hop, tab_notes , textEdit_notes , Hop::notes ),
+ EDITOR_FIELD_NORM(Hop, label_alpha , lineEdit_alpha , Hop::alpha_pct , 1),
+ EDITOR_FIELD_NORM(Hop, label_inventory , lineEdit_inventory , Ingredient::totalInventory, 1, WhenToWriteField::Late),
+ EDITOR_FIELD_COPQ(Hop, label_amountType , comboBox_amountType , Ingredient::totalInventory, lineEdit_inventory, WhenToWriteField::Never),
+ EDITOR_FIELD_NORM(Hop, label_beta , lineEdit_beta , Hop::beta_pct , 1),
+ EDITOR_FIELD_NORM(Hop, label_HSI , lineEdit_HSI , Hop::hsi_pct , 0),
+ EDITOR_FIELD_NORM(Hop, label_origin , lineEdit_origin , Hop::origin ),
+ EDITOR_FIELD_NORM(Hop, label_humulene , lineEdit_humulene , Hop::humulene_pct , 2),
+ EDITOR_FIELD_NORM(Hop, label_caryophyllene , lineEdit_caryophyllene , Hop::caryophyllene_pct , 2),
+ EDITOR_FIELD_NORM(Hop, label_cohumulone , lineEdit_cohumulone , Hop::cohumulone_pct , 2),
+ EDITOR_FIELD_NORM(Hop, label_myrcene , lineEdit_myrcene , Hop::myrcene_pct , 2),
+ EDITOR_FIELD_ENUM(Hop, label_type , comboBox_hopType , Hop::type ),
+ EDITOR_FIELD_ENUM(Hop, label_form , comboBox_hopForm , Hop::form ),
+ // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞
+ EDITOR_FIELD_NORM(Hop, label_producer , lineEdit_producer , Hop::producer ),
+ EDITOR_FIELD_NORM(Hop, label_productId , lineEdit_productId , Hop::productId ),
+ EDITOR_FIELD_NORM(Hop, label_year , lineEdit_year , Hop::year ),
+ EDITOR_FIELD_NORM(Hop, label_totalOil_mlPer100g, lineEdit_totalOil_mlPer100g, Hop::totalOil_mlPer100g ),
+ EDITOR_FIELD_NORM(Hop, label_farnesene , lineEdit_farnesene , Hop::farnesene_pct , 2),
+ EDITOR_FIELD_NORM(Hop, label_geraniol , lineEdit_geraniol , Hop::geraniol_pct , 2),
+ EDITOR_FIELD_NORM(Hop, label_bPinene , lineEdit_bPinene , Hop::bPinene_pct , 2),
+ EDITOR_FIELD_NORM(Hop, label_linalool , lineEdit_linalool , Hop::linalool_pct , 2),
+ EDITOR_FIELD_NORM(Hop, label_limonene , lineEdit_limonene , Hop::limonene_pct , 2),
+ EDITOR_FIELD_NORM(Hop, label_nerol , lineEdit_nerol , Hop::nerol_pct , 2),
+ EDITOR_FIELD_NORM(Hop, label_pinene , lineEdit_pinene , Hop::pinene_pct , 2),
+ EDITOR_FIELD_NORM(Hop, label_polyphenols , lineEdit_polyphenols , Hop::polyphenols_pct , 2),
+ EDITOR_FIELD_NORM(Hop, label_xanthohumol , lineEdit_xanthohumol , Hop::xanthohumol_pct , 2),
+ });
return;
}
HopEditor::~HopEditor() = default;
-void HopEditor::writeFieldsToEditItem() {
- this->m_editItem->setName (this->lineEdit_name ->text ());
- this->m_editItem->setAlpha_pct (this->lineEdit_alpha ->getNonOptValue());
- this->m_editItem->setBeta_pct (this->lineEdit_beta ->getOptValue ());
- this->m_editItem->setHsi_pct (this->lineEdit_HSI ->getOptValue ());
- this->m_editItem->setOrigin (this->lineEdit_origin ->text ());
- this->m_editItem->setHumulene_pct (this->lineEdit_humulene ->getOptValue ());
- this->m_editItem->setCaryophyllene_pct(this->lineEdit_caryophyllene->getOptValue ());
- this->m_editItem->setCohumulone_pct (this->lineEdit_cohumulone ->getOptValue ());
- this->m_editItem->setMyrcene_pct (this->lineEdit_myrcene ->getOptValue ());
- this->m_editItem->setSubstitutes (this->textEdit_substitutes ->toPlainText ());
- this->m_editItem->setNotes (this->textEdit_notes ->toPlainText ());
-
- this->m_editItem->setType (this->comboBox_hopType ->getOptValue());
- this->m_editItem->setForm (this->comboBox_hopForm ->getOptValue());
-
- // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞
-/// this->m_editItem->setAmountIsWeight (this->checkBox_amountIsWeight ->isChecked ());
- this->m_editItem->setProducer (this->lineEdit_producer ->text ());
- this->m_editItem->setProductId (this->lineEdit_productId ->text ());
- this->m_editItem->setYear (this->lineEdit_year ->text ());
- this->m_editItem->setTotalOil_mlPer100g(this->lineEdit_totalOil_mlPer100g->getOptValue