diff --git a/CHANGES.markdown b/CHANGES.markdown index 71dda20cb..270170a84 100644 --- a/CHANGES.markdown +++ b/CHANGES.markdown @@ -13,8 +13,20 @@ happens, so I'm now setting it to a slightly arbitrary time early in the morning * Additional methods for calculating IBU * We'll list other new features here... +## v4.0.6 +Bug fixes for the 4.0.5 release (ie bugs in 4.0.5 are fixed in this 4.0.6 release). + +### New Features +* None + +### Bug Fixes +* Ingredient inventory edits not saved [832](https://github.com/Brewtarget/brewtarget/issues/832) + +### Release Timestamp +Mon, 30 Sep 2024 04:00:06 +0100 + ## v4.0.5 -Minor bug fixes for the 4.0.4 release (ie bugs in 4.0.4 are fixed in this 4.0.5 release). +Bug fixes for the 4.0.4 release (ie bugs in 4.0.4 are fixed in this 4.0.5 release). ### New Features * None @@ -26,7 +38,7 @@ Minor bug fixes for the 4.0.4 release (ie bugs in 4.0.4 are fixed in this 4.0.5 * BeerXML/BeerJSON Recipe export does not match selected type [835](https://github.com/Brewtarget/brewtarget/issues/835) ### Release Timestamp -Sat, 28 Sep 2024 04:00:05 +0100 +Sun, 29 Sep 2024 04:00:05 +0100 ## v4.0.4 Minor bug fixes for the 4.0.3 release (ie bugs in 4.0.3 are fixed in this 4.0.4 release). diff --git a/CMakeLists.txt b/CMakeLists.txt index ba7c04214..3f233d9ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,7 +109,7 @@ endif() #======================================================================================================================= # It's simplest to keep the project name all lower-case as it means we can use a lot more of the default settings for # Linux packaging (where directory names etc are expected to be all lower-case). -project(brewtarget VERSION 4.0.5 LANGUAGES CXX) +project(brewtarget VERSION 4.0.6 LANGUAGES CXX) message(STATUS "Building ${PROJECT_NAME} version ${PROJECT_VERSION}") message(STATUS "PROJECT_SOURCE_DIR is ${PROJECT_SOURCE_DIR}") # Sometimes we do need the capitalised version of the project name diff --git a/meson.build b/meson.build index 33af8f724..87083e334 100644 --- a/meson.build +++ b/meson.build @@ -189,7 +189,7 @@ # # project('brewtarget', 'cpp', - version: '4.0.5', + version: '4.0.6', license: 'GPL-3.0-or-later', meson_version: '>=0.60.0', default_options : ['cpp_std=c++20', diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 595d5f05a..7d87376b4 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(); } } diff --git a/src/WaterDialog.cpp b/src/WaterDialog.cpp index 734735e87..b8c3c8014 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 80786ea71..318e2ab31 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; diff --git a/src/database/ObjectStoreTyped.cpp b/src/database/ObjectStoreTyped.cpp index b66c8c1d0..9341792fc 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 }, } }; diff --git a/src/editors/BoilEditor.cpp b/src/editors/BoilEditor.cpp index 342a80b6b..ec2b92169 100755 --- a/src/editors/BoilEditor.cpp +++ b/src/editors/BoilEditor.cpp @@ -12,7 +12,7 @@ * * You should have received a copy of the GNU General Public License along with this program. If not, see * . - ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌*/ + =====================================================================================================================*/ #include "editors/BoilEditor.h" #include @@ -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 c4cca6126..040df3420 100755 --- 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 89b5eac2c..f4d4899cc 100755 --- 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 3f92a16a6..87c595ce9 100755 --- 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 11ae9fa00..2a5d45f5f 100755 --- 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 000000000..0e3a5dac6 --- /dev/null +++ b/src/editors/EditorBaseField.h @@ -0,0 +1,414 @@ +/*╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ + * editors/EditorBaseField.h is part of Brewtarget, and is copyright the following authors 2024: + * • Matt Young + * + * Brewtarget 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. + * + * Brewtarget 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 0eef13c1c..000000000 --- a/src/editors/EditorWithRecipeBase.h +++ /dev/null @@ -1,119 +0,0 @@ -/*╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ - * editors/EditorWithRecipeBase.h is part of Brewtarget, and is copyright the following authors 2024: - * • Matt Young - * - * Brewtarget 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. - * - * Brewtarget 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 ba9b07a94..51d63210e 100755 --- 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 adac358bb..b58b2070d 100755 --- 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,6 +56,7 @@ public slots: bool validateBeforeSave(); private: + void postReadFieldsFromEditItem(std::optional propName); double calcBatchSize(); }; diff --git a/src/editors/FermentableEditor.cpp b/src/editors/FermentableEditor.cpp index e5480d4de..a15a4a833 100755 --- 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 a26d1b558..60a9b3be0 100755 --- 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 a2f001319..52b7dc185 100755 --- 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 111f7cc42..cfcf1d0cb 100755 --- 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 dea1847ed..b80ef2b37 100755 --- 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 33b22c15c..7bd06820c 100755 --- 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 d88d079ab..f16a02217 100755 --- 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()); - this->m_editItem->setFarnesene_pct (this->lineEdit_farnesene ->getOptValue()); - this->m_editItem->setGeraniol_pct (this->lineEdit_geraniol ->getOptValue()); - this->m_editItem->setBPinene_pct (this->lineEdit_bPinene ->getOptValue()); - this->m_editItem->setLinalool_pct (this->lineEdit_linalool ->getOptValue()); - this->m_editItem->setLimonene_pct (this->lineEdit_limonene ->getOptValue()); - this->m_editItem->setNerol_pct (this->lineEdit_nerol ->getOptValue()); - this->m_editItem->setPinene_pct (this->lineEdit_pinene ->getOptValue()); - this->m_editItem->setPolyphenols_pct (this->lineEdit_polyphenols ->getOptValue()); - this->m_editItem->setXanthohumol_pct (this->lineEdit_xanthohumol ->getOptValue()); - return; -} - -void HopEditor::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 Hop). - // - // 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 HopEditor::readFieldsFromEditItem(std::optional propName) { - if (!propName || *propName == PropertyNames::Hop::type ) { this->comboBox_hopType ->setValue (m_editItem->type ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::form ) { this->comboBox_hopForm ->setValue (m_editItem->form ()); if (propName) { return; } } - 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::Hop::origin ) { this->lineEdit_origin ->setTextCursor(m_editItem->origin ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::alpha_pct ) { this->lineEdit_alpha ->setQuantity (m_editItem->alpha_pct ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::beta_pct ) { this->lineEdit_beta ->setQuantity (m_editItem->beta_pct ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::hsi_pct ) { this->lineEdit_HSI ->setQuantity (m_editItem->hsi_pct ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::humulene_pct ) { this->lineEdit_humulene ->setQuantity (m_editItem->humulene_pct ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::caryophyllene_pct) { this->lineEdit_caryophyllene->setQuantity (m_editItem->caryophyllene_pct()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::cohumulone_pct ) { this->lineEdit_cohumulone ->setQuantity (m_editItem->cohumulone_pct ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::myrcene_pct ) { this->lineEdit_myrcene ->setQuantity (m_editItem->myrcene_pct ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::substitutes ) { this->textEdit_substitutes ->setPlainText (m_editItem->substitutes ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::notes ) { this->textEdit_notes ->setPlainText (m_editItem->notes ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Ingredient::totalInventory) { this->lineEdit_inventory->setAmount (m_editItem->totalInventory ()); - this->comboBox_amountType->autoSetFromControlledField(); - if (propName) { return; } } - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - if (!propName || *propName == PropertyNames::Hop::producer ) { this->lineEdit_producer ->setText (m_editItem->producer ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::productId ) { this->lineEdit_productId ->setText (m_editItem->productId ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::year ) { this->lineEdit_year ->setText (m_editItem->year ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::totalOil_mlPer100g) { this->lineEdit_totalOil_mlPer100g->setQuantity (m_editItem->totalOil_mlPer100g()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::farnesene_pct ) { this->lineEdit_farnesene ->setQuantity(m_editItem->farnesene_pct ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::geraniol_pct ) { this->lineEdit_geraniol ->setQuantity(m_editItem->geraniol_pct ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::bPinene_pct ) { this->lineEdit_bPinene ->setQuantity(m_editItem->bPinene_pct ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::linalool_pct ) { this->lineEdit_linalool ->setQuantity(m_editItem->linalool_pct ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::limonene_pct ) { this->lineEdit_limonene ->setQuantity(m_editItem->limonene_pct ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::nerol_pct ) { this->lineEdit_nerol ->setQuantity(m_editItem->nerol_pct ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::pinene_pct ) { this->lineEdit_pinene ->setQuantity(m_editItem->pinene_pct ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::polyphenols_pct) { this->lineEdit_polyphenols->setQuantity(m_editItem->polyphenols_pct()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Hop::xanthohumol_pct) { this->lineEdit_xanthohumol->setQuantity(m_editItem->xanthohumol_pct()); 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(HopEditor) +EDITOR_COMMON_CODE(Hop) diff --git a/src/editors/HopEditor.h b/src/editors/HopEditor.h index 6aa1fd15b..3ee5de8ec 100755 --- a/src/editors/HopEditor.h +++ b/src/editors/HopEditor.h @@ -26,6 +26,7 @@ #include "editors/EditorBase.h" #include "model/Hop.h" +#define HopEditorOptions EditorBaseOptions{ .nameTab = true, .idDisplay = true } /*! * \class HopEditor * @@ -34,10 +35,12 @@ * See comment on EditorBase::connectSignalsAndSlots for why we need to have \c public, not \c private * inheritance from the Ui base. */ -class HopEditor : public QDialog, public Ui::hopEditor, public EditorBase { +class HopEditor : public QDialog, + public Ui::hopEditor, + public EditorBase { Q_OBJECT - EDITOR_COMMON_DECL(Hop) + EDITOR_COMMON_DECL(Hop, HopEditorOptions) }; #endif diff --git a/src/editors/MashStepEditor.cpp b/src/editors/MashStepEditor.cpp index 9e1db697a..97e6457ba 100755 --- a/src/editors/MashStepEditor.cpp +++ b/src/editors/MashStepEditor.cpp @@ -21,74 +21,39 @@ #include "MainWindow.h" #include "measurement/Unit.h" -MashStepEditor::MashStepEditor(QWidget* parent) : +MashStepEditor::MashStepEditor(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(MashStepEditor, label_name , lineEdit_name , MashStep, PropertyNames::NamedEntity::name ); - SMART_FIELD_INIT(MashStepEditor, label_amount , lineEdit_amount , MashStep, PropertyNames:: MashStep::amount_l ); - SMART_FIELD_INIT(MashStepEditor, label_infuseTemp , lineEdit_infuseTemp , MashStep, PropertyNames:: MashStep::infuseTemp_c , 1); - SMART_FIELD_INIT(MashStepEditor, label_stepTemp , lineEdit_stepTemp , MashStep, PropertyNames:: Step::startTemp_c , 1); - SMART_FIELD_INIT(MashStepEditor, label_stepTime , lineEdit_stepTime , MashStep, PropertyNames:: Step::stepTime_mins , 0); - SMART_FIELD_INIT(MashStepEditor, label_rampTime , lineEdit_rampTime , MashStep, PropertyNames:: Step::rampTime_mins , 0); - SMART_FIELD_INIT(MashStepEditor, label_endTemp , lineEdit_endTemp , MashStep, PropertyNames:: Step::endTemp_c , 1); - // ⮜⮜⮜ All three below added for BeerJSON support ⮞⮞⮞ - SMART_FIELD_INIT(MashStepEditor, label_thickness , lineEdit_thickness , MashStep, PropertyNames:: MashStep::liquorToGristRatio_lKg, 1); - SMART_FIELD_INIT(MashStepEditor, label_startAcidity , lineEdit_startAcidity , MashStep, PropertyNames:: Step::startAcidity_pH , 1); - SMART_FIELD_INIT(MashStepEditor, label_endAcidity , lineEdit_endAcidity , MashStep, PropertyNames:: Step::endAcidity_pH , 1); - - BT_COMBO_BOX_INIT(MashStepEditor, comboBox_mashStepType, MashStep, type); - - this->connectSignalsAndSlots(); + this->postSetupUiInit({ + // + // As explained in model/MashStep.h, there is only one amount for a MashStep, and it is accessed via + // MashStep::amount_l. The decoctionAmount_l and infuseAmount_l properties are only used for reading and writing + // BeerXML. + // + // We retain infuseTemp_c for now, even though it is not part of BeerJSON. TBD whether it is needed longer-term. + // + EDITOR_FIELD_NORM(MashStep, label_name , lineEdit_name , NamedEntity::name ), + EDITOR_FIELD_NORM(MashStep, label_description , textEdit_description , Step::description ), + EDITOR_FIELD_NORM(MashStep, label_amount , lineEdit_amount , MashStep::amount_l ), + EDITOR_FIELD_NORM(MashStep, label_stepTemp , lineEdit_stepTemp , Step::startTemp_c , 1), + EDITOR_FIELD_NORM(MashStep, label_stepTime , lineEdit_stepTime , Step::stepTime_mins , 0), + EDITOR_FIELD_NORM(MashStep, label_rampTime , lineEdit_rampTime , Step::rampTime_mins , 0), + EDITOR_FIELD_NORM(MashStep, label_endTemp , lineEdit_endTemp , Step::endTemp_c , 1), + EDITOR_FIELD_NORM(MashStep, label_infuseTemp , lineEdit_infuseTemp , MashStep::infuseTemp_c , 1), + EDITOR_FIELD_NORM(MashStep, label_startAcidity, lineEdit_startAcidity, Step::startAcidity_pH , 1), + EDITOR_FIELD_NORM(MashStep, label_endAcidity , lineEdit_endAcidity , Step::endAcidity_pH , 1), + EDITOR_FIELD_NORM(MashStep, label_thickness , lineEdit_thickness , MashStep::liquorToGristRatio_lKg, 1), + EDITOR_FIELD_ENUM(MashStep, label_mashStepType, comboBox_mashStepType, MashStep::type ), + }); // This is extra for this editor - connect(this->comboBox_mashStepType, &QComboBox::currentTextChanged, this, &MashStepEditor::grayOutStuff); + this->connect(this->comboBox_mashStepType, &QComboBox::currentTextChanged, this, &MashStepEditor::grayOutStuff); return; } MashStepEditor::~MashStepEditor() = default; -void MashStepEditor::readFieldsFromEditItem(std::optional propName) { - if (!propName || *propName == PropertyNames::NamedEntity::name ) { this->lineEdit_name ->setTextCursor(m_editItem->name ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::MashStep::type ) { this->comboBox_mashStepType->setValue (m_editItem->type ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::MashStep::infuseAmount_l ) { this->lineEdit_amount ->setQuantity (m_editItem->amount_l ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::MashStep::infuseTemp_c ) { this->lineEdit_infuseTemp ->setQuantity (m_editItem->infuseTemp_c ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames:: Step::startTemp_c ) { this->lineEdit_stepTemp ->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; } } - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - if (!propName || *propName == PropertyNames::MashStep::liquorToGristRatio_lKg) { this->lineEdit_thickness ->setQuantity (m_editItem->liquorToGristRatio_lKg()); 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:: Step::description ) { this->textEdit_description ->setPlainText (m_editItem->description ()); if (propName) { return; } } - return; -} - -void MashStepEditor::writeFieldsToEditItem() { - this->m_editItem->setName (this->lineEdit_name->text()); - this->m_editItem->setType (this->comboBox_mashStepType->getNonOptValue()); - this->m_editItem->setAmount_l (this->lineEdit_amount ->getNonOptCanonicalQty()); - this->m_editItem->setInfuseTemp_c (this->lineEdit_infuseTemp ->getOptCanonicalQty ()); - this->m_editItem->setStartTemp_c (this->lineEdit_stepTemp ->getNonOptCanonicalQty()); - this->m_editItem->setStepTime_mins (this->lineEdit_stepTime ->getNonOptCanonicalQty()); - this->m_editItem->setRampTime_mins (this->lineEdit_rampTime ->getOptCanonicalQty ()); - this->m_editItem->setEndTemp_c (this->lineEdit_endTemp ->getOptCanonicalQty ()); - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - this->m_editItem->setLiquorToGristRatio_lKg(this->lineEdit_thickness ->getOptCanonicalQty ()); - this->m_editItem->setStartAcidity_pH (this->lineEdit_startAcidity->getOptCanonicalQty ()); - this->m_editItem->setEndAcidity_pH (this->lineEdit_endAcidity ->getOptCanonicalQty ()); - this->m_editItem->setDescription (this->textEdit_description ->toPlainText ()); - return; -} - -void MashStepEditor::writeLateFieldsToEditItem() { - // Nothing to do here - return; -} - void MashStepEditor::grayOutStuff([[maybe_unused]] QString const & text) { auto const msType = this->comboBox_mashStepType->getNonOptValue(); switch (msType) { @@ -111,4 +76,4 @@ void MashStepEditor::grayOutStuff([[maybe_unused]] QString const & text) { } // Insert the boiler-plate stuff that we cannot do in EditorBase -EDITOR_COMMON_CODE(MashStepEditor) +EDITOR_COMMON_CODE(MashStep) diff --git a/src/editors/MashStepEditor.h b/src/editors/MashStepEditor.h index 32f1c9522..508a9699b 100755 --- a/src/editors/MashStepEditor.h +++ b/src/editors/MashStepEditor.h @@ -27,15 +27,18 @@ #include "editors/EditorBase.h" #include "model/MashStep.h" +#define MashStepEditorOptions EditorBaseOptions{ } /*! * \class MashStepEditor * * \brief View/controller dialog for editing mash steps. */ -class MashStepEditor : public QDialog, public Ui::mashStepEditor, public EditorBase { +class MashStepEditor : public QDialog, + public Ui::mashStepEditor, + public EditorBase { Q_OBJECT - EDITOR_COMMON_DECL(MashStep) + EDITOR_COMMON_DECL(MashStep, MashStepEditorOptions) public slots: /*! diff --git a/src/editors/MiscEditor.cpp b/src/editors/MiscEditor.cpp index 91ecba42d..6295ace65 100755 --- a/src/editors/MiscEditor.cpp +++ b/src/editors/MiscEditor.cpp @@ -28,73 +28,32 @@ #include "database/ObjectStoreWrapper.h" #include "measurement/Unit.h" -MiscEditor::MiscEditor(QWidget * parent) : +MiscEditor::MiscEditor(QWidget * parent, QString const editorName) : QDialog(parent), - EditorBase() { + EditorBase(editorName) { setupUi(this); - - tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs); - - SMART_FIELD_INIT(MiscEditor, label_name , lineEdit_name , Misc, PropertyNames::NamedEntity::name); - SMART_FIELD_INIT(MiscEditor, label_inventory, lineEdit_inventory, Misc, PropertyNames::Ingredient::totalInventory, 1); - - BT_COMBO_BOX_INIT(MiscEditor, comboBox_type, Misc, type); - - BT_COMBO_BOX_INIT_COPQ(MiscEditor, comboBox_amountType, Misc, PropertyNames::Ingredient::totalInventory, lineEdit_inventory); - - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - SMART_FIELD_INIT(MiscEditor, label_producer , lineEdit_producer , Misc, PropertyNames::Misc::producer ); - SMART_FIELD_INIT(MiscEditor, label_productId, lineEdit_productId, Misc, PropertyNames::Misc::productId ); - - 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(Misc, label_name , lineEdit_name , NamedEntity::name ), + EDITOR_FIELD_NORM(Misc, label_inventory , lineEdit_inventory , Ingredient::totalInventory, 1, WhenToWriteField::Late), + EDITOR_FIELD_COPQ(Misc, label_amountType, comboBox_amountType, Ingredient::totalInventory, lineEdit_inventory, WhenToWriteField::Never), + EDITOR_FIELD_ENUM(Misc, label_type , comboBox_type , Misc::type ), + EDITOR_FIELD_NORM(Misc, tab_useFor , textEdit_useFor , Misc::useFor ), + EDITOR_FIELD_NORM(Misc, tab_notes , textEdit_notes , Misc::notes ), + // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ + EDITOR_FIELD_NORM(Misc, label_producer , lineEdit_producer , Misc::producer ), + EDITOR_FIELD_NORM(Misc, label_productId , lineEdit_productId , Misc::productId ), + }); return; } MiscEditor::~MiscEditor() = default; -void MiscEditor::writeFieldsToEditItem() { - this->m_editItem->setType(this->comboBox_type->getNonOptValue()); - this->m_editItem->setName (this->lineEdit_name ->text ()); - this->m_editItem->setUseFor (this->textEdit_useFor ->toPlainText ()); - this->m_editItem->setNotes (this->textEdit_notes ->toPlainText ()); - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - this->m_editItem->setProducer (this->lineEdit_producer ->text ()); - this->m_editItem->setProductId (this->lineEdit_productId ->text ()); - return; -} - -void MiscEditor::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 Misc). - // - // 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 MiscEditor::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::Misc::type ) { this->comboBox_type ->setValue (m_editItem->type ()); 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::Misc::useFor ) { this->textEdit_useFor ->setPlainText (m_editItem->useFor ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Misc::notes ) { this->textEdit_notes ->setPlainText (m_editItem->notes ()); if (propName) { return; } } - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - if (!propName || *propName == PropertyNames::Misc::producer ) { this->lineEdit_producer ->setTextCursor(m_editItem->producer ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Misc::productId ) { this->lineEdit_productId->setTextCursor(m_editItem->productId()); 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(MiscEditor) +EDITOR_COMMON_CODE(Misc) diff --git a/src/editors/MiscEditor.h b/src/editors/MiscEditor.h index 50f62406f..11d5e6036 100755 --- a/src/editors/MiscEditor.h +++ b/src/editors/MiscEditor.h @@ -29,6 +29,7 @@ #include "editors/EditorBase.h" #include "model/Misc.h" +#define MiscEditorOptions EditorBaseOptions{ .nameTab = true, .idDisplay = true } /*! * \class MiscEditor * @@ -37,10 +38,12 @@ * See comment on EditorBase::connectSignalsAndSlots for why we need to have \c public, not \c private * inheritance from the Ui base. */ -class MiscEditor : public QDialog, public Ui::miscEditor, public EditorBase { +class MiscEditor : public QDialog, + public Ui::miscEditor, + public EditorBase { Q_OBJECT - EDITOR_COMMON_DECL(Misc) + EDITOR_COMMON_DECL(Misc, MiscEditorOptions) }; #endif diff --git a/src/editors/NamedMashEditor.h b/src/editors/NamedMashEditor.h index a351710e1..531b12036 100755 --- a/src/editors/NamedMashEditor.h +++ b/src/editors/NamedMashEditor.h @@ -43,7 +43,7 @@ class Equipment; * * \brief View/controller dialog for editing a mash. * - * See also \c MashEditor + * See also \c MashEditor (into which I think this should be subsumed!) */ class NamedMashEditor : public QDialog, public Ui::namedMashEditor { Q_OBJECT diff --git a/src/editors/StyleEditor.cpp b/src/editors/StyleEditor.cpp index b90ce3e65..ede476672 100755 --- a/src/editors/StyleEditor.cpp +++ b/src/editors/StyleEditor.cpp @@ -25,115 +25,48 @@ #include "measurement/Unit.h" #include "sortFilterProxyModels/StyleSortFilterProxyModel.h" -StyleEditor::StyleEditor(QWidget* parent) : +StyleEditor::StyleEditor(QWidget* parent, QString const editorName) : QDialog{parent}, - EditorBase() { + EditorBase(editorName) { setupUi(this); - + this->postSetupUiInit({ + // Note that the Min / Max pairs of entry fields each share a label (which is shown to the left of both fields) + EDITOR_FIELD_NORM(Style, label_name , lineEdit_name , NamedEntity::name ), + EDITOR_FIELD_NORM(Style, label_category , lineEdit_category , Style::category ), + EDITOR_FIELD_NORM(Style, label_categoryNumber , lineEdit_categoryNumber , Style::categoryNumber ), + EDITOR_FIELD_NORM(Style, label_styleLetter , lineEdit_styleLetter , Style::styleLetter ), + EDITOR_FIELD_NORM(Style, label_styleGuide , lineEdit_styleGuide , Style::styleGuide ), + EDITOR_FIELD_NORM(Style, label_og , lineEdit_ogMin , Style::ogMin ), + EDITOR_FIELD_NORM(Style, label_og , lineEdit_ogMax , Style::ogMax ), + EDITOR_FIELD_NORM(Style, label_fg , lineEdit_fgMin , Style::fgMin ), + EDITOR_FIELD_NORM(Style, label_fg , lineEdit_fgMax , Style::fgMax ), + EDITOR_FIELD_NORM(Style, label_ibu , lineEdit_ibuMin , Style::ibuMin , 0), + EDITOR_FIELD_NORM(Style, label_ibu , lineEdit_ibuMax , Style::ibuMax , 0), + EDITOR_FIELD_NORM(Style, label_color , lineEdit_colorMin , Style::colorMin_srm ), + EDITOR_FIELD_NORM(Style, label_color , lineEdit_colorMax , Style::colorMax_srm ), + EDITOR_FIELD_NORM(Style, label_carb , lineEdit_carbMin , Style::carbMin_vol , 0), + EDITOR_FIELD_NORM(Style, label_carb , lineEdit_carbMax , Style::carbMax_vol , 0), + EDITOR_FIELD_NORM(Style, label_abv , lineEdit_abvMin , Style::abvMin_pct , 1), + EDITOR_FIELD_NORM(Style, label_abv , lineEdit_abvMax , Style::abvMax_pct , 1), + EDITOR_FIELD_ENUM(Style, label_type , comboBox_type , Style::type ), + EDITOR_FIELD_NORM(Style, tab_ingredients , textEdit_ingredients , Style::ingredients ), + EDITOR_FIELD_NORM(Style, tab_examples , textEdit_examples , Style::examples ), + EDITOR_FIELD_NORM(Style, tab_notes , textEdit_notes , Style::notes ), + // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ + EDITOR_FIELD_NORM(Style, tab_aroma , textEdit_aroma , Style::aroma ), + EDITOR_FIELD_NORM(Style, tab_appearance , textEdit_appearance , Style::appearance ), + EDITOR_FIELD_NORM(Style, tab_flavor , textEdit_flavor , Style::flavor ), + EDITOR_FIELD_NORM(Style, tab_mouthfeel , textEdit_mouthfeel , Style::mouthfeel ), + EDITOR_FIELD_NORM(Style, tab_overallImpression , textEdit_overallImpression, Style::overallImpression), + }); + + // EditorBase doesn't do this for us because we don't put the style name in the tab (possibly because it can be quite + // long). this->tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs); - - // Note that the Min / Max pairs of entry fields each share a label (which is shown to the left of both fields) - SMART_FIELD_INIT(StyleEditor, label_name , lineEdit_name , Style, PropertyNames::NamedEntity::name ); - SMART_FIELD_INIT(StyleEditor, label_category , lineEdit_category , Style, PropertyNames::Style::category ); - SMART_FIELD_INIT(StyleEditor, label_categoryNumber, lineEdit_categoryNumber, Style, PropertyNames::Style::categoryNumber ); - SMART_FIELD_INIT(StyleEditor, label_styleLetter , lineEdit_styleLetter , Style, PropertyNames::Style::styleLetter ); - SMART_FIELD_INIT(StyleEditor, label_styleGuide , lineEdit_styleGuide , Style, PropertyNames::Style::styleGuide ); - SMART_FIELD_INIT(StyleEditor, label_og , lineEdit_ogMin , Style, PropertyNames::Style::ogMin ); - SMART_FIELD_INIT(StyleEditor, label_og , lineEdit_ogMax , Style, PropertyNames::Style::ogMax ); - SMART_FIELD_INIT(StyleEditor, label_fg , lineEdit_fgMin , Style, PropertyNames::Style::fgMin ); - SMART_FIELD_INIT(StyleEditor, label_fg , lineEdit_fgMax , Style, PropertyNames::Style::fgMax ); - SMART_FIELD_INIT(StyleEditor, label_ibu , lineEdit_ibuMin , Style, PropertyNames::Style::ibuMin , 0); - SMART_FIELD_INIT(StyleEditor, label_ibu , lineEdit_ibuMax , Style, PropertyNames::Style::ibuMax , 0); - SMART_FIELD_INIT(StyleEditor, label_color , lineEdit_colorMin , Style, PropertyNames::Style::colorMin_srm ); - SMART_FIELD_INIT(StyleEditor, label_color , lineEdit_colorMax , Style, PropertyNames::Style::colorMax_srm ); - SMART_FIELD_INIT(StyleEditor, label_carb , lineEdit_carbMin , Style, PropertyNames::Style::carbMin_vol , 0); - SMART_FIELD_INIT(StyleEditor, label_carb , lineEdit_carbMax , Style, PropertyNames::Style::carbMax_vol , 0); - SMART_FIELD_INIT(StyleEditor, label_abv , lineEdit_abvMin , Style, PropertyNames::Style::abvMin_pct , 1); - SMART_FIELD_INIT(StyleEditor, label_abv , lineEdit_abvMax , Style, PropertyNames::Style::abvMax_pct , 1); - - BT_COMBO_BOX_INIT(StyleEditor, comboBox_type, Style, type); - - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - // [Nothing here as all new fields are text-only] - - this->connectSignalsAndSlots(); return; } StyleEditor::~StyleEditor() = default; -void StyleEditor::writeFieldsToEditItem() { - - m_editItem->setName (this->lineEdit_name ->text ()); - m_editItem->setCategory (this->lineEdit_category ->text ()); - m_editItem->setCategoryNumber(this->lineEdit_categoryNumber->text ()); - m_editItem->setStyleLetter (this->lineEdit_styleLetter ->text ()); - m_editItem->setStyleGuide (this->lineEdit_styleGuide ->text ()); - m_editItem->setType (this->comboBox_type ->getNonOptValue()); - m_editItem->setOgMin (this->lineEdit_ogMin ->getNonOptCanonicalQty ()); - m_editItem->setOgMax (this->lineEdit_ogMax ->getNonOptCanonicalQty ()); - m_editItem->setFgMin (this->lineEdit_fgMin ->getNonOptCanonicalQty ()); - m_editItem->setFgMax (this->lineEdit_fgMax ->getNonOptCanonicalQty ()); - m_editItem->setIbuMin (this->lineEdit_ibuMin ->getNonOptValue ()); - m_editItem->setIbuMax (this->lineEdit_ibuMax ->getNonOptValue ()); - m_editItem->setColorMin_srm (this->lineEdit_colorMin ->getNonOptCanonicalQty ()); - m_editItem->setColorMax_srm (this->lineEdit_colorMax ->getNonOptCanonicalQty ()); - m_editItem->setCarbMin_vol (this->lineEdit_carbMin ->getNonOptCanonicalQty ()); - m_editItem->setCarbMax_vol (this->lineEdit_carbMax ->getNonOptCanonicalQty ()); - m_editItem->setAbvMin_pct (this->lineEdit_abvMin ->getNonOptValue ()); - m_editItem->setAbvMax_pct (this->lineEdit_abvMax ->getNonOptValue ()); -/// this->m_editItem->setProfile (textEdit_profile ->toPlainText ()); - m_editItem->setIngredients (this->textEdit_ingredients ->toPlainText ()); - m_editItem->setExamples (this->textEdit_examples ->toPlainText ()); - m_editItem->setNotes (this->textEdit_notes ->toPlainText ()); - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - m_editItem->setAroma (this->textEdit_aroma ->toPlainText ()); - m_editItem->setAppearance (this->textEdit_appearance ->toPlainText ()); - m_editItem->setFlavor (this->textEdit_flavor ->toPlainText ()); - m_editItem->setMouthfeel (this->textEdit_mouthfeel ->toPlainText ()); - m_editItem->setOverallImpression(this->textEdit_overallImpression->toPlainText ()); - - return; -} - -void StyleEditor::writeLateFieldsToEditItem() { - // Nothing to do here for Style - return; -} - -void StyleEditor::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::Style::category ) { lineEdit_category ->setText (m_editItem->category ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::categoryNumber) { lineEdit_categoryNumber->setText (m_editItem->categoryNumber()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::styleLetter ) { lineEdit_styleLetter ->setText (m_editItem->styleLetter ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::styleGuide ) { lineEdit_styleGuide ->setText (m_editItem->styleGuide ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::type ) { comboBox_type ->setValue (m_editItem->type ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::ogMin ) { lineEdit_ogMin ->setQuantity (m_editItem->ogMin ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::ogMax ) { lineEdit_ogMax ->setQuantity (m_editItem->ogMax ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::fgMin ) { lineEdit_fgMin ->setQuantity (m_editItem->fgMin ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::fgMax ) { lineEdit_fgMax ->setQuantity (m_editItem->fgMax ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::ibuMin ) { lineEdit_ibuMin ->setQuantity (m_editItem->ibuMin ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::ibuMax ) { lineEdit_ibuMax ->setQuantity (m_editItem->ibuMax ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::colorMin_srm ) { lineEdit_colorMin ->setQuantity (m_editItem->colorMin_srm ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::colorMax_srm ) { lineEdit_colorMax ->setQuantity (m_editItem->colorMax_srm ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::carbMin_vol ) { lineEdit_carbMin ->setQuantity (m_editItem->carbMin_vol ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::carbMax_vol ) { lineEdit_carbMax ->setQuantity (m_editItem->carbMax_vol ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::abvMin_pct ) { lineEdit_abvMin ->setQuantity (m_editItem->abvMin_pct ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::abvMax_pct ) { lineEdit_abvMax ->setQuantity (m_editItem->abvMax_pct ()); if (propName) { return; } } -/// if (!propName || *propName == PropertyNames::Style::profile ) { textEdit_profile ->setText (m_editItem->profile ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::ingredients ) { textEdit_ingredients ->setText (m_editItem->ingredients ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::examples ) { textEdit_examples ->setText (m_editItem->examples ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::notes ) { textEdit_notes ->setText (m_editItem->notes ()); if (propName) { return; } } - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - if (!propName || *propName == PropertyNames::Style::aroma ) { textEdit_aroma ->setText (m_editItem->aroma ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::appearance ) { textEdit_appearance ->setText (m_editItem->appearance ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::flavor ) { textEdit_flavor ->setText (m_editItem->flavor ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::mouthfeel ) { textEdit_mouthfeel ->setText (m_editItem->mouthfeel ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Style::overallImpression) { textEdit_overallImpression->setText (m_editItem->overallImpression()); if (propName) { return; } } - - return; -} - // Insert the boiler-plate stuff that we cannot do in EditorBase -EDITOR_COMMON_CODE(StyleEditor) +EDITOR_COMMON_CODE(Style) diff --git a/src/editors/StyleEditor.h b/src/editors/StyleEditor.h index e4fc83782..12966e9ab 100755 --- a/src/editors/StyleEditor.h +++ b/src/editors/StyleEditor.h @@ -27,6 +27,7 @@ #include "editors/EditorBase.h" #include "model/Style.h" +#define StyleEditorOptions EditorBaseOptions{ } /*! * \class StyleEditor * @@ -35,10 +36,12 @@ * See comment on EditorBase::connectSignalsAndSlots for why we need to have \c public, not \c private * inheritance from the Ui base. */ -class StyleEditor : public QDialog, public Ui::styleEditor, public EditorBase { +class StyleEditor : public QDialog, + public Ui::styleEditor, + public EditorBase { Q_OBJECT - EDITOR_COMMON_DECL(Style) + EDITOR_COMMON_DECL(Style, StyleEditorOptions) }; #endif diff --git a/src/editors/WaterEditor.cpp b/src/editors/WaterEditor.cpp index 0f58f4f0f..43723540c 100755 --- a/src/editors/WaterEditor.cpp +++ b/src/editors/WaterEditor.cpp @@ -25,69 +25,31 @@ #include "database/ObjectStoreWrapper.h" #include "model/Water.h" -// This private implementation class holds all private non-virtual members of WaterEditor -class WaterEditor::impl { -public: - /** - * Constructor - */ - impl(QString const editorName) : editorName{editorName}, - observedWater{}, - editedWater{} { - return; - } - - /** - * Destructor - */ - ~impl() = default; - - QString const editorName; - - // This is the Water object we are "observing" and to which our edits will be committed if and when the user clicks - // OK - std::shared_ptr observedWater; - // This is a temporary copy of the "observed" Water that holds the live edits (which will be saved if the user clicks - // OK and lost if the user clicks Cancel) - std::unique_ptr editedWater; -}; - -WaterEditor::WaterEditor(QWidget *parent, - QString const editorName) : QDialog(parent), - pimpl{std::make_unique(editorName)} { - setupUi(this); - - SMART_FIELD_INIT(WaterEditor, label_ca , lineEdit_ca , Water, PropertyNames::Water::calcium_ppm , 2); - SMART_FIELD_INIT(WaterEditor, label_cl , lineEdit_cl , Water, PropertyNames::Water::chloride_ppm , 2); - SMART_FIELD_INIT(WaterEditor, label_mg , lineEdit_mg , Water, PropertyNames::Water::magnesium_ppm , 2); - SMART_FIELD_INIT(WaterEditor, label_so4 , lineEdit_so4 , Water, PropertyNames::Water::sulfate_ppm , 2); - SMART_FIELD_INIT(WaterEditor, label_na , lineEdit_na , Water, PropertyNames::Water::sodium_ppm , 2); - SMART_FIELD_INIT(WaterEditor, label_alk , lineEdit_alk , Water, PropertyNames::Water::alkalinity_ppm, 2); - SMART_FIELD_INIT(WaterEditor, label_pH , lineEdit_ph , Water, PropertyNames::Water::ph , 2); - // TODO: Finish adding extra fields below! - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ -// SMART_FIELD_INIT(WaterEditor, label_carbonate, lineEdit_carbonate, Water, PropertyNames::Water::carbonate_ppm , 2); -// SMART_FIELD_INIT(WaterEditor, label_potassium, lineEdit_potassium, Water, PropertyNames::Water::potassium_ppm , 2); -// SMART_FIELD_INIT(WaterEditor, label_iron , lineEdit_iron , Water, PropertyNames::Water::iron_ppm , 2); -// SMART_FIELD_INIT(WaterEditor, label_nitrate , lineEdit_nitrate , Water, PropertyNames::Water::nitrate_ppm , 2); -// SMART_FIELD_INIT(WaterEditor, label_nitrite , lineEdit_nitrite , Water, PropertyNames::Water::nitrite_ppm , 2); -// SMART_FIELD_INIT(WaterEditor, label_flouride , lineEdit_flouride , Water, PropertyNames::Water::flouride_ppm , 2); - - // .:TBD:. The QLineEdit::textEdited and QPlainTextEdit::textChanged signals below are sent somewhat more frequently - // than we really need - ie every time you type a character in the name or notes field. We should perhaps look at - // changing the corresponding field types... - connect(this->buttonBox, &QDialogButtonBox::accepted, this, &WaterEditor::saveAndClose ); - connect(this->buttonBox, &QDialogButtonBox::rejected, this, &WaterEditor::clearAndClose ); - connect(this->comboBox_alk, &QComboBox::currentTextChanged, this, &WaterEditor::inputFieldModified); - connect(this->lineEdit_alk, &SmartLineEdit::textModified, this, &WaterEditor::inputFieldModified); - connect(this->lineEdit_ca, &SmartLineEdit::textModified, this, &WaterEditor::inputFieldModified); - connect(this->lineEdit_cl, &SmartLineEdit::textModified, this, &WaterEditor::inputFieldModified); - connect(this->lineEdit_mg, &SmartLineEdit::textModified, this, &WaterEditor::inputFieldModified); - connect(this->lineEdit_na, &SmartLineEdit::textModified, this, &WaterEditor::inputFieldModified); - connect(this->lineEdit_name, &QLineEdit::textEdited, this, &WaterEditor::inputFieldModified); - connect(this->lineEdit_ph, &SmartLineEdit::textModified, this, &WaterEditor::inputFieldModified); - connect(this->lineEdit_so4, &SmartLineEdit::textModified, this, &WaterEditor::inputFieldModified); - connect(this->plainTextEdit_notes, &QPlainTextEdit::textChanged, this, &WaterEditor::inputFieldModified); +WaterEditor::WaterEditor(QWidget *parent, QString const editorName) : + QDialog(parent), + EditorBase(editorName) { + this->setupUi(this); + this->postSetupUiInit( + { + EDITOR_FIELD_NORM(Water, label_name , lineEdit_name , NamedEntity::name ), + EDITOR_FIELD_NORM(Water, label_notes , textEdit_notes , Water::notes ), + EDITOR_FIELD_NORM(Water, label_ca , lineEdit_ca , Water::calcium_ppm , 2), + EDITOR_FIELD_NORM(Water, label_cl , lineEdit_cl , Water::chloride_ppm , 2), + EDITOR_FIELD_NORM(Water, label_mg , lineEdit_mg , Water::magnesium_ppm , 2), + EDITOR_FIELD_NORM(Water, label_so4 , lineEdit_so4 , Water::sulfate_ppm , 2), + EDITOR_FIELD_NORM(Water, label_na , lineEdit_na , Water::sodium_ppm , 2), + EDITOR_FIELD_NORM(Water, label_alk , lineEdit_alk , Water::alkalinity_ppm , 2), + EDITOR_FIELD_NORM(Water, label_pH , lineEdit_ph , Water::ph , 2), + EDITOR_FIELD_NORM(Water, label_alkalinityAsHCO3, boolCombo_alkalinityAsHCO3, Water::alkalinityAsHCO3, tr("CaCO3"), tr("HCO3")), + // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ + EDITOR_FIELD_NORM(Water, label_carbonate , lineEdit_carbonate , Water::carbonate_ppm , 2), + EDITOR_FIELD_NORM(Water, label_potassium , lineEdit_potassium , Water::potassium_ppm , 2), + EDITOR_FIELD_NORM(Water, label_iron , lineEdit_iron , Water::iron_ppm , 2), + EDITOR_FIELD_NORM(Water, label_nitrate , lineEdit_nitrate , Water::nitrate_ppm , 2), + EDITOR_FIELD_NORM(Water, label_nitrite , lineEdit_nitrite , Water::nitrite_ppm , 2), + EDITOR_FIELD_NORM(Water, label_fluoride , lineEdit_fluoride , Water::fluoride_ppm , 2), + } + ); this->waterEditRadarChart->init( tr("PPM"), @@ -108,216 +70,39 @@ WaterEditor::WaterEditor(QWidget *parent, //WaterEditor::~WaterEditor() = default; WaterEditor::~WaterEditor() { qDebug() << Q_FUNC_INFO << "Cleaning up"; - if (this->pimpl->observedWater) { + if (this->m_editItem) { qDebug() << - Q_FUNC_INFO << this->pimpl->editorName << ": Was observing" << this->pimpl->observedWater->name() << - "#" << this->pimpl->observedWater->key() << " @" << static_cast(this->pimpl->observedWater.get()) << - " (use count" << this->pimpl->observedWater.use_count() << ")"; + Q_FUNC_INFO << this->m_editorName << ": Was observing" << this->m_editItem->name() << + "#" << this->m_editItem->key() << " @" << static_cast(this->m_editItem.get()) << + " (use count" << this->m_editItem.use_count() << ")"; } - if (this->pimpl->editedWater) { + if (this->m_liveEditItem) { qDebug() << - Q_FUNC_INFO << this->pimpl->editorName << ": Was editing" << this->pimpl->editedWater->name() << - "#" << this->pimpl->editedWater->key() << " @" << static_cast(this->pimpl->editedWater.get()); + Q_FUNC_INFO << this->m_editorName << ": Was editing" << this->m_liveEditItem->name() << + "#" << this->m_liveEditItem->key() << " @" << static_cast(this->m_liveEditItem.get()); } return; } -void WaterEditor::setWater(std::shared_ptr water) { - - if (this->pimpl->observedWater) { - qDebug() << - Q_FUNC_INFO << this->pimpl->editorName << ": Stop observing" << this->pimpl->observedWater->name() << - "#" << this->pimpl->observedWater->key() << " @" << static_cast(this->pimpl->observedWater.get()) << - " (use count" << this->pimpl->observedWater.use_count() << ")"; - disconnect(this->pimpl->observedWater.get(), nullptr, this, nullptr); - this->pimpl->observedWater.reset(); - } - - if (water) { - this->pimpl->observedWater = water; - qDebug() << - Q_FUNC_INFO << this->pimpl->editorName << ": Now observing" << this->pimpl->observedWater->name() << - "#" << this->pimpl->observedWater->key() << " @" << static_cast(this->pimpl->observedWater.get()) << - " (use count" << this->pimpl->observedWater.use_count() << ")"; - this->waterEditRadarChart->addSeries(tr("Current"), Qt::darkGreen, *this->pimpl->observedWater); - connect(this->pimpl->observedWater.get(), &NamedEntity::changed, this, &WaterEditor::changed); - - // Make a copy of the Water object we are observing - this->pimpl->editedWater = std::make_unique(*this->pimpl->observedWater); -/// this->pimpl->editedWater->setAmount(0.0); - this->waterEditRadarChart->addSeries(tr("Modified"), Qt::green, *this->pimpl->editedWater); +void WaterEditor::postSetEditItem() { + if (this->m_editItem) { + // Note that we don't need to remove the old series from any previous Water objects as the call to addSeries will + // replace them. + this->waterEditRadarChart->addSeries(tr("Current"), Qt::darkGreen, *this->m_editItem); - this->showChanges(); - } else { - qDebug() << Q_FUNC_INFO << this->pimpl->editorName << ": Observing Nothing"; + this->waterEditRadarChart->addSeries(tr("Modified"), Qt::green, *this->m_liveEditItem); } - return; } -void WaterEditor::newWater(QString folder) { - QString name = QInputDialog::getText(this, tr("Water name"), - tr("Water name:")); - if (name.isEmpty()) { - return; - } - - qDebug() << Q_FUNC_INFO << this->pimpl->editorName << ": Creating new Water, " << name; - - this->setWater(std::make_shared(name)); - if (!folder.isEmpty()) { - this->pimpl->observedWater->setFolder(folder); - } - - setVisible(true); - - return; -} - -void WaterEditor::showChanges(QMetaProperty const * prop) { - - if (!this->pimpl->observedWater) { - return; - } - - QString propName; - - bool updateAll = false; - - if (prop == nullptr) { - qDebug() << Q_FUNC_INFO << this->pimpl->editorName << ": Update all"; - updateAll = true; - } else { - propName = prop->name(); - qDebug() << Q_FUNC_INFO << this->pimpl->editorName << ": Changed" << propName; - } - - if (updateAll || propName == PropertyNames::NamedEntity::name ) { this->lineEdit_name->setText (this->pimpl->observedWater->name ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::calcium_ppm ) { this->lineEdit_ca ->setQuantity(this->pimpl->observedWater->calcium_ppm ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::magnesium_ppm ) { this->lineEdit_mg ->setQuantity(this->pimpl->observedWater->magnesium_ppm ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::sulfate_ppm ) { this->lineEdit_so4 ->setQuantity(this->pimpl->observedWater->sulfate_ppm ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::sodium_ppm ) { this->lineEdit_na ->setQuantity(this->pimpl->observedWater->sodium_ppm ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::chloride_ppm ) { this->lineEdit_cl ->setQuantity(this->pimpl->observedWater->chloride_ppm ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::bicarbonate_ppm ) { this->lineEdit_alk ->setQuantity(this->pimpl->observedWater->bicarbonate_ppm()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::ph ) { this->lineEdit_ph ->setQuantity(this->pimpl->observedWater->ph ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::alkalinityAsHCO3) { - bool typeless = this->pimpl->observedWater->alkalinityAsHCO3(); - this->comboBox_alk->setCurrentIndex(comboBox_alk->findText(typeless ? "HCO3" : "CaCO3")); - if (!updateAll) { return; } - } - if (updateAll || propName == PropertyNames::Water::notes ) { this->plainTextEdit_notes->setPlainText(this->pimpl->observedWater->notes() ); if (!updateAll) { return; } } - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - if (updateAll || propName == PropertyNames::Water::carbonate_ppm ) { this->lineEdit_alk ->setQuantity(this->pimpl->observedWater->carbonate_ppm()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::potassium_ppm ) { this->lineEdit_alk ->setQuantity(this->pimpl->observedWater->potassium_ppm()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::iron_ppm ) { this->lineEdit_alk ->setQuantity(this->pimpl->observedWater->iron_ppm ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::nitrate_ppm ) { this->lineEdit_alk ->setQuantity(this->pimpl->observedWater->nitrate_ppm ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::nitrite_ppm ) { this->lineEdit_alk ->setQuantity(this->pimpl->observedWater->nitrite_ppm ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::flouride_ppm ) { this->lineEdit_alk ->setQuantity(this->pimpl->observedWater->flouride_ppm ()); if (!updateAll) { return; } } - return; -} - -void WaterEditor::inputFieldModified() { - // - // What we're doing here is, if one of the input fields on the dialog is modified, we update the corresponding - // field(s) on this->pimpl->editedWater and replot the radar chart. That way the user can see the "shape" of their - // changes in real time. +void WaterEditor::postInputFieldModified() { // - // When we come to close the window, depending on whether the user clicked "OK" or "Cancel" we then either copy the - // changes to the "observed" water (this->pimpl->observedWater) or discard them (resetting this->pimpl->editedWater - // to be the same as this->pimpl->observedWater). + // Strictly speaking we don't always need to replot the radar chart - eg if a text field changed it doesn't affect + // the chart - but, for the moment, we just keep things simple and always replot. // - QObject const * const signalSender = this->sender(); - // Usually leave the next line commented as otherwise get too much logging when user is typing in notes or name - // fields. -// qDebug() << Q_FUNC_INFO << this->pimpl->editorName << ": signal from" << signalSender; - if (signalSender && signalSender->parent() == this) { - // .:TBD:. Need to get to the bottom of the relationship between Water::alkalinity and Water::bicarbonate_ppm. It - // feels wrong that we just set both from the same input, but probably needs some more profound thought - // about what exactly correct behaviour should be. - if (signalSender == this->comboBox_alk) {this->pimpl->editedWater->setAlkalinityAsHCO3(this->comboBox_alk ->currentText() == QString("HCO3"));} - else if (signalSender == this->lineEdit_alk) {this->pimpl->editedWater->setBicarbonate_ppm (this->lineEdit_alk ->getNonOptCanonicalQty()); // NB continues on next line! - this->pimpl->editedWater->setAlkalinity_ppm (this->lineEdit_alk ->getNonOptCanonicalQty()); } - else if (signalSender == this->lineEdit_ca) {this->pimpl->editedWater->setCalcium_ppm (this->lineEdit_ca ->getNonOptCanonicalQty()); } - else if (signalSender == this->lineEdit_cl) {this->pimpl->editedWater->setChloride_ppm (this->lineEdit_cl ->getNonOptCanonicalQty()); } - else if (signalSender == this->lineEdit_mg) {this->pimpl->editedWater->setMagnesium_ppm (this->lineEdit_mg ->getNonOptCanonicalQty()); } - else if (signalSender == this->lineEdit_na) {this->pimpl->editedWater->setSodium_ppm (this->lineEdit_na ->getNonOptCanonicalQty()); } - else if (signalSender == this->lineEdit_name) {this->pimpl->editedWater->setName (this->lineEdit_name->text()); } - else if (signalSender == this->lineEdit_ph) {this->pimpl->editedWater->setPh (this->lineEdit_ph ->getNonOptCanonicalQty()); } - else if (signalSender == this->lineEdit_so4) {this->pimpl->editedWater->setSulfate_ppm (this->lineEdit_so4 ->getNonOptCanonicalQty()); } - else if (signalSender == this->plainTextEdit_notes) {this->pimpl->editedWater->setNotes (this->plainTextEdit_notes->toPlainText()); } - else { - // If we get here, it's probably a coding error - qWarning() << Q_FUNC_INFO << "Unrecognised child"; - } - - // - // Strictly speaking we don't always need to replot the radar chart - eg if a text field changed it doesn't affect - // the chart - but, for the moment, we just keep things simple and always replot. - // - this->waterEditRadarChart->replot(); - } - return; -} - -void WaterEditor::changed(QMetaProperty prop, QVariant /*val*/) { - if (sender() == this->pimpl->observedWater.get()) { - this->showChanges(&prop); - } - this->waterEditRadarChart->replot(); return; } -void WaterEditor::saveAndClose() { - qDebug() << Q_FUNC_INFO << this->pimpl->editorName; - if (!this->pimpl->observedWater) { - // For the moment, if we weren't given a Water object (via setWater) then we don't try to save any changes when - // the editor is closed. Arguably, if the user has actually filled in a bunch of data, then we should use that - // to create and save a new Water object. - qDebug() << Q_FUNC_INFO << "Save and close with no Water specified, so discarding any inputs"; - return; - } - - // Apply all the edits - if (this->pimpl->editedWater) { - *this->pimpl->observedWater = *this->pimpl->editedWater; - qDebug() << - Q_FUNC_INFO << this->pimpl->editorName << ": Applied edits to Water #" << this->pimpl->observedWater->key() << - ":" << this->pimpl->observedWater->name(); - } - - // - // TBD: When we're called from WaterDialog, it is that window that is responsible for adding new Water objects to the - // Recipe (which results in the Water object being saved in the DB). Saving the Water object means the current - // logic in WaterDialog won't pick up that it needs to be added to the Recipe. - // - if (this->pimpl->observedWater->key() < 0) { - qDebug() << Q_FUNC_INFO << "Writing new Water:" << this->pimpl->observedWater->name(); - ObjectStoreWrapper::insert(this->pimpl->observedWater); - } - - setVisible(false); - return; -} - -void WaterEditor::clearAndClose() { - qDebug() << Q_FUNC_INFO << this->pimpl->editorName; - - // At this point, we want to clear edits, but we _don't_ want to stop observing the Water that's been given to us as - // our creator (eg WaterDialog) may redisplay us without a repeat call to setWater. - - // This reverts all the input fields - this->showChanges(); - - // Revert all the edits in our temporary copy of the "observed" Water - if (this->pimpl->observedWater && this->pimpl->editedWater) { - *this->pimpl->editedWater = *this->pimpl->observedWater; - qDebug() << - Q_FUNC_INFO << this->pimpl->editorName << ": Discarded edits to Water #" << - this->pimpl->observedWater->key() << ":" << this->pimpl->observedWater->name(); - } - - setVisible(false); // Hide the window. - return; -} +EDITOR_COMMON_CODE(Water) diff --git a/src/editors/WaterEditor.h b/src/editors/WaterEditor.h index 5adfcb373..7365def20 100755 --- a/src/editors/WaterEditor.h +++ b/src/editors/WaterEditor.h @@ -29,40 +29,25 @@ #include "ui_waterEditor.h" -// Forward declarations. -class Water; +#include "editors/EditorBase.h" +#include "model/Water.h" +#define WaterEditorOptions EditorBaseOptions{ .liveEditItem = true } /*! * \class WaterEditor * * \brief View/controller class for creating and modifying water records. */ -class WaterEditor : public QDialog, public Ui::waterEditor { +class WaterEditor : public QDialog, + public Ui::waterEditor, + public EditorBase { Q_OBJECT public: - WaterEditor(QWidget *parent = nullptr, QString const editorName = "Unnamed"); - virtual ~WaterEditor(); - - /*! - * Sets the water we want to observe. - * - * \param water If \c nullptr then stop observing - */ - void setWater(std::shared_ptr water); - - void newWater(QString folder); - -public slots: - void showChanges(QMetaProperty const * prop = nullptr); - void inputFieldModified(); - void changed(QMetaProperty, QVariant); - void saveAndClose(); - void clearAndClose(); + EDITOR_COMMON_DECL(Water, WaterEditorOptions) private: - // Private implementation details - see https://herbsutter.com/gotw/_100/ - class impl; - std::unique_ptr pimpl; + void postSetEditItem(); + void postInputFieldModified(); }; #endif diff --git a/src/editors/YeastEditor.cpp b/src/editors/YeastEditor.cpp index 6ee3b150d..611564410 100755 --- a/src/editors/YeastEditor.cpp +++ b/src/editors/YeastEditor.cpp @@ -29,124 +29,52 @@ #include "database/ObjectStoreWrapper.h" #include "measurement/Unit.h" -YeastEditor::YeastEditor(QWidget * parent) : +YeastEditor::YeastEditor(QWidget * parent, QString const editorName) : QDialog(parent), - EditorBase() { + EditorBase(editorName) { setupUi(this); + 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 Yeast). + // + // 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(Yeast, label_name , lineEdit_name , NamedEntity::name ), + EDITOR_FIELD_NORM(Yeast, label_laboratory , lineEdit_laboratory , Yeast::laboratory ), + EDITOR_FIELD_NORM(Yeast, label_inventory , lineEdit_inventory , Ingredient::totalInventory, 1, WhenToWriteField::Late), + EDITOR_FIELD_COPQ(Yeast, label_amountType , comboBox_amountType , Ingredient::totalInventory, lineEdit_inventory, WhenToWriteField::Never), + EDITOR_FIELD_NORM(Yeast, label_productId , lineEdit_productId , Yeast::productId ), + EDITOR_FIELD_NORM(Yeast, label_minTemperature, lineEdit_minTemperature , Yeast::minTemperature_c, 1 ), + EDITOR_FIELD_NORM(Yeast, label_maxTemperature, lineEdit_maxTemperature , Yeast::maxTemperature_c, 1 ), + EDITOR_FIELD_NORM(Yeast, label_maxReuse , lineEdit_maxReuse , Yeast::maxReuse ), + EDITOR_FIELD_ENUM(Yeast, label_type , comboBox_yeastType , Yeast::type ), + EDITOR_FIELD_ENUM(Yeast, label_form , comboBox_yeastForm , Yeast::form ), + EDITOR_FIELD_ENUM(Yeast, label_flocculation , comboBox_yeastFlocculation, Yeast::flocculation ), + EDITOR_FIELD_NORM(Yeast, tab_bestFor , textEdit_bestFor , Yeast::bestFor ), + EDITOR_FIELD_NORM(Yeast, tab_notes , textEdit_notes , Yeast::notes ), + // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ + EDITOR_FIELD_NORM(Yeast, label_alcoholTolerance , lineEdit_alcoholTolerance , Yeast::alcoholTolerance_pct , 1), + EDITOR_FIELD_NORM(Yeast, label_attenuationMin , lineEdit_attenuationMin , Yeast::attenuationMin_pct , 1), + EDITOR_FIELD_NORM(Yeast, label_attenuationMax , lineEdit_attenuationMax , Yeast::attenuationMax_pct , 1), + EDITOR_FIELD_NORM(Yeast, label_phenolicOffFlavorPositive, boolCombo_phenolicOffFlavorPositive, Yeast::phenolicOffFlavorPositive), + EDITOR_FIELD_NORM(Yeast, label_glucoamylasePositive , boolCombo_glucoamylasePositive , Yeast::glucoamylasePositive ), + EDITOR_FIELD_NORM(Yeast, label_killerProducingK1Toxin , boolCombo_killerProducingK1Toxin , Yeast::killerProducingK1Toxin ), + EDITOR_FIELD_NORM(Yeast, label_killerProducingK2Toxin , boolCombo_killerProducingK2Toxin , Yeast::killerProducingK2Toxin ), + EDITOR_FIELD_NORM(Yeast, label_killerProducingK28Toxin , boolCombo_killerProducingK28Toxin , Yeast::killerProducingK28Toxin ), + EDITOR_FIELD_NORM(Yeast, label_killerProducingKlusToxin , boolCombo_killerProducingKlusToxin , Yeast::killerProducingKlusToxin ), + EDITOR_FIELD_NORM(Yeast, label_killerNeutral , boolCombo_killerNeutral , Yeast::killerNeutral ) + + } + ); - this->tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs); - - SMART_FIELD_INIT(YeastEditor, label_name , lineEdit_name , Yeast, PropertyNames::NamedEntity::name ); - SMART_FIELD_INIT(YeastEditor, label_laboratory , lineEdit_laboratory , Yeast, PropertyNames::Yeast::laboratory ); - SMART_FIELD_INIT(YeastEditor, label_inventory , lineEdit_inventory , Yeast, PropertyNames::Ingredient::totalInventory, 1); - SMART_FIELD_INIT(YeastEditor, label_productId , lineEdit_productId , Yeast, PropertyNames::Yeast::productId ); - SMART_FIELD_INIT(YeastEditor, label_minTemperature, lineEdit_minTemperature, Yeast, PropertyNames::Yeast::minTemperature_c, 1); - SMART_FIELD_INIT(YeastEditor, label_maxTemperature, lineEdit_maxTemperature, Yeast, PropertyNames::Yeast::maxTemperature_c, 1); - SMART_FIELD_INIT(YeastEditor, label_maxReuse , lineEdit_maxReuse , Yeast, PropertyNames::Yeast::maxReuse ); - - BT_COMBO_BOX_INIT(HopEditor, comboBox_yeastType , Yeast, type ); - BT_COMBO_BOX_INIT(HopEditor, comboBox_yeastForm , Yeast, form ); - BT_COMBO_BOX_INIT(HopEditor, comboBox_yeastFlocculation, Yeast, flocculation); - - BT_COMBO_BOX_INIT_COPQ(YeastEditor, comboBox_amountType, Yeast, PropertyNames::Ingredient::totalInventory, lineEdit_inventory); - - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - - SMART_FIELD_INIT(YeastEditor, label_alcoholTolerance, lineEdit_alcoholTolerance, Yeast, PropertyNames::Yeast::alcoholTolerance_pct, 1); - SMART_FIELD_INIT(YeastEditor, label_attenuationMin , lineEdit_attenuationMin , Yeast, PropertyNames::Yeast::attenuationMin_pct , 1); - SMART_FIELD_INIT(YeastEditor, label_attenuationMax , lineEdit_attenuationMax , Yeast, PropertyNames::Yeast::attenuationMax_pct , 1); - - BT_BOOL_COMBO_BOX_INIT(YeastEditor, boolCombo_phenolicOffFlavorPositive, Yeast, phenolicOffFlavorPositive); - BT_BOOL_COMBO_BOX_INIT(YeastEditor, boolCombo_glucoamylasePositive , Yeast, glucoamylasePositive ); - BT_BOOL_COMBO_BOX_INIT(YeastEditor, boolCombo_killerProducingK1Toxin , Yeast, killerProducingK1Toxin ); - BT_BOOL_COMBO_BOX_INIT(YeastEditor, boolCombo_killerProducingK2Toxin , Yeast, killerProducingK2Toxin ); - BT_BOOL_COMBO_BOX_INIT(YeastEditor, boolCombo_killerProducingK28Toxin , Yeast, killerProducingK28Toxin ); - BT_BOOL_COMBO_BOX_INIT(YeastEditor, boolCombo_killerProducingKlusToxin , Yeast, killerProducingKlusToxin ); - BT_BOOL_COMBO_BOX_INIT(YeastEditor, boolCombo_killerNeutral , Yeast, killerNeutral ); - - this->connectSignalsAndSlots(); return; } YeastEditor::~YeastEditor() = default; -void YeastEditor::writeFieldsToEditItem() { - - this->m_editItem->setName (lineEdit_name ->text() ); - this->m_editItem->setType (comboBox_yeastType ->getNonOptValue() ); - this->m_editItem->setForm (comboBox_yeastForm ->getNonOptValue() ); - this->m_editItem->setLaboratory (lineEdit_laboratory ->text() ); - this->m_editItem->setProductId (lineEdit_productId ->text() ); - this->m_editItem->setMinTemperature_c(lineEdit_minTemperature ->getOptCanonicalQty() ); // ⮜⮜⮜ Optional in BeerXML ⮞⮞⮞ - this->m_editItem->setMaxTemperature_c(lineEdit_maxTemperature ->getOptCanonicalQty() ); // ⮜⮜⮜ Optional in BeerXML ⮞⮞⮞ - this->m_editItem->setFlocculation (comboBox_yeastFlocculation->getOptValue()); - this->m_editItem->setMaxReuse (lineEdit_maxReuse ->getOptValue() ); // ⮜⮜⮜ Optional in BeerXML ⮞⮞⮞ - this->m_editItem->setBestFor (textEdit_bestFor ->toPlainText() ); - this->m_editItem->setNotes (textEdit_notes ->toPlainText() ); - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - this->m_editItem->setAlcoholTolerance_pct (this->lineEdit_alcoholTolerance ->getOptValue()); - this->m_editItem->setAttenuationMin_pct (this->lineEdit_attenuationMin ->getOptValue()); - this->m_editItem->setAttenuationMax_pct (this->lineEdit_attenuationMax ->getOptValue()); - this->m_editItem->setPhenolicOffFlavorPositive(this->boolCombo_phenolicOffFlavorPositive->getOptBoolValue ()); - this->m_editItem->setGlucoamylasePositive (this->boolCombo_glucoamylasePositive ->getOptBoolValue ()); - this->m_editItem->setKillerProducingK1Toxin (this->boolCombo_killerProducingK1Toxin ->getOptBoolValue ()); - this->m_editItem->setKillerProducingK2Toxin (this->boolCombo_killerProducingK2Toxin ->getOptBoolValue ()); - this->m_editItem->setKillerProducingK28Toxin (this->boolCombo_killerProducingK28Toxin ->getOptBoolValue ()); - this->m_editItem->setKillerProducingKlusToxin (this->boolCombo_killerProducingKlusToxin ->getOptBoolValue ()); - this->m_editItem->setKillerNeutral (this->boolCombo_killerNeutral ->getOptBoolValue ()); - - return; -} - -void YeastEditor::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 Misc). - // - // 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 YeastEditor::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::Yeast::type ) { this->comboBox_yeastType ->setValue (m_editItem->type ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Yeast::form ) { this->comboBox_yeastForm ->setValue (m_editItem->form ()); 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::Yeast::laboratory ) { this->lineEdit_laboratory ->setText (m_editItem->laboratory ()); // Continues to next line - this->lineEdit_laboratory ->setCursorPosition(0) ; if (propName) { return; } } - if (!propName || *propName == PropertyNames::Yeast::productId ) { this->lineEdit_productId ->setText (m_editItem->productId ()); // Continues to next line - this->lineEdit_productId ->setCursorPosition(0) ; if (propName) { return; } } - if (!propName || *propName == PropertyNames::Yeast::minTemperature_c ) { this->lineEdit_minTemperature ->setQuantity (m_editItem->minTemperature_c()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Yeast::maxTemperature_c ) { this->lineEdit_maxTemperature ->setQuantity (m_editItem->maxTemperature_c()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Yeast::flocculation ) { this->comboBox_yeastFlocculation->setValue (m_editItem->flocculation ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Yeast::maxReuse ) { this->lineEdit_maxReuse ->setQuantity (m_editItem->maxReuse ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Yeast::bestFor ) { this->textEdit_bestFor ->setPlainText(m_editItem->bestFor ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Yeast::notes ) { this->textEdit_notes ->setPlainText(m_editItem->notes ()); if (propName) { return; } } - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - if (!propName || *propName == PropertyNames::Yeast::alcoholTolerance_pct) { this->lineEdit_alcoholTolerance->setQuantity(m_editItem->alcoholTolerance_pct()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Yeast::attenuationMin_pct ) { this->lineEdit_attenuationMin ->setQuantity(m_editItem->attenuationMin_pct ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Yeast::attenuationMax_pct ) { this->lineEdit_attenuationMax ->setQuantity(m_editItem->attenuationMax_pct ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Yeast::phenolicOffFlavorPositive) { this->boolCombo_phenolicOffFlavorPositive->setValue(m_editItem->phenolicOffFlavorPositive()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Yeast::glucoamylasePositive ) { this->boolCombo_glucoamylasePositive ->setValue(m_editItem->glucoamylasePositive ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Yeast::killerProducingK1Toxin ) { this->boolCombo_killerProducingK1Toxin ->setValue(m_editItem->killerProducingK1Toxin ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Yeast::killerProducingK2Toxin ) { this->boolCombo_killerProducingK2Toxin ->setValue(m_editItem->killerProducingK2Toxin ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Yeast::killerProducingK28Toxin ) { this->boolCombo_killerProducingK28Toxin ->setValue(m_editItem->killerProducingK28Toxin ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Yeast::killerProducingKlusToxin ) { this->boolCombo_killerProducingKlusToxin ->setValue(m_editItem->killerProducingKlusToxin ()); if (propName) { return; } } - if (!propName || *propName == PropertyNames::Yeast::killerNeutral ) { this->boolCombo_killerNeutral ->setValue(m_editItem->killerNeutral ()); 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(YeastEditor) +EDITOR_COMMON_CODE(Yeast) diff --git a/src/editors/YeastEditor.h b/src/editors/YeastEditor.h index 0e2cf543a..6d79fe9f9 100755 --- a/src/editors/YeastEditor.h +++ b/src/editors/YeastEditor.h @@ -29,6 +29,7 @@ #include "editors/EditorBase.h" #include "model/Yeast.h" +#define YeastEditorOptions EditorBaseOptions{ .nameTab = true, .idDisplay = true } /*! * \class YeastEditor * @@ -37,10 +38,12 @@ * See comment on EditorBase::connectSignalsAndSlots for why we need to have \c public, not \c private * inheritance from the Ui base. */ -class YeastEditor : public QDialog, public Ui::yeastEditor, public EditorBase { +class YeastEditor : public QDialog, + public Ui::yeastEditor, + public EditorBase { Q_OBJECT - EDITOR_COMMON_DECL(Yeast) + EDITOR_COMMON_DECL(Yeast, YeastEditorOptions) }; #endif diff --git a/src/model/Fermentation.h b/src/model/Fermentation.h index 0f099415f..7abf735fd 100755 --- a/src/model/Fermentation.h +++ b/src/model/Fermentation.h @@ -76,9 +76,9 @@ class Fermentation : public NamedEntity, //=================================================== PROPERTIES ==================================================== //! \brief Folder. See model/FolderBase for implementation of the getter & setter. - Q_PROPERTY(QString folder READ folder WRITE setFolder) - Q_PROPERTY(QString description READ description WRITE setDescription) - Q_PROPERTY(QString notes READ notes WRITE setNotes ) + Q_PROPERTY(QString folder READ folder WRITE setFolder ) + Q_PROPERTY(QString description READ description WRITE setDescription) + Q_PROPERTY(QString notes READ notes WRITE setNotes ) //! \brief The individual fermentation steps. (See \c StepOwnerBase for getter/setter implementation.) Q_PROPERTY(QList> fermentationSteps READ fermentationSteps WRITE setFermentationSteps STORED false) diff --git a/src/model/IngredientAmount.h b/src/model/IngredientAmount.h index e3a48dfa5..36f3cee2a 100755 --- a/src/model/IngredientAmount.h +++ b/src/model/IngredientAmount.h @@ -23,7 +23,7 @@ #include "utils/EnumStringMapping.h" #include "utils/TypeLookup.h" -//╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ +//====================================================================================================================== //========================================== Start of property name constants ========================================== // See comment in model/NamedEntity.h #define AddPropertyName(property) namespace PropertyNames::IngredientAmount { BtStringConst const property{#property}; } @@ -34,7 +34,7 @@ AddPropertyName(quantity) // Only quantity and unit should be used in database m AddPropertyName(unit ) // Only quantity and unit should be used in database mappings #undef AddPropertyName //=========================================== End of property name constants =========================================== -//╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ +//====================================================================================================================== /** * \brief Represents an amount of an ingredient. These amounts are used in two places: in the \c RecipeAddition @@ -198,8 +198,8 @@ class IngredientAmount : public CuriouslyRecurringTemplateBasedoSetQuantity(val.quantity); this->doSetUnit (val.unit ); diff --git a/src/model/IngredientBase.h b/src/model/IngredientBase.h index 5f9548972..20914e853 100755 --- a/src/model/IngredientBase.h +++ b/src/model/IngredientBase.h @@ -35,14 +35,25 @@ class IngredientBase : public CuriouslyRecurringTemplateBase(this->derived())->amount(); + auto inventory = InventoryTools::getInventory(this->derived()); + // Normally leave this log statement commented out as it generates too many lines in the log file +// qDebug() << +// Q_FUNC_INFO << "Inventory object:" << *inventory << "; ingredient ID:" << inventory->ingredientId() << +// "; amount:" << inventory->amount(); + return inventory->amount(); } /** * \brief Used to implement Ingredient::setTotalInventory() for Ingredient subclass (ie Derived) */ void doSetTotalInventory(Measurement::Amount const val) { + // InventoryTools::getInventory will have ensured the object returned here is in the database, even if it was + // newly-created. auto inventory = InventoryTools::getInventory(this->derived()); + // Normally leave this log statement commented out as it generates too many lines in the log file +// qDebug() << +// Q_FUNC_INFO << "Inventory object:" << *inventory << "; change amount from" << inventory->amount() << "to" << +// val; inventory->setAmount(val); return; } diff --git a/src/model/Inventory.h b/src/model/Inventory.h index e14628ed7..5138fa504 100644 --- a/src/model/Inventory.h +++ b/src/model/Inventory.h @@ -261,18 +261,21 @@ TypeLookup const Inventory##IngredientName::typeLookup { }; \ static_assert(std::is_base_of::value); \ Inventory##IngredientName::Inventory##IngredientName() : \ - Inventory(), \ - IngredientAmount() { \ - return; \ + Inventory{}, \ + IngredientAmount{} { \ + CONSTRUCTOR_END \ + return; \ } \ Inventory##IngredientName::Inventory##IngredientName(NamedParameterBundle const & npb) : \ Inventory {npb}, \ IngredientAmount{npb} { \ + CONSTRUCTOR_END \ return; \ } \ Inventory##IngredientName::Inventory##IngredientName(Inventory##IngredientName const & other) : \ Inventory {other}, \ IngredientAmount{other} { \ + CONSTRUCTOR_END \ return; \ } \ Inventory##IngredientName::~Inventory##IngredientName() = default; \ diff --git a/src/model/MashStep.h b/src/model/MashStep.h index c193e7907..8a4b6826d 100644 --- a/src/model/MashStep.h +++ b/src/model/MashStep.h @@ -136,7 +136,9 @@ class MashStep : public Step, public StepBase { * \brief The infusion temp in C. ⮜⮜⮜ Not part of BeerXML; optional in BeerJSON ⮞⮞⮞ * * An infusion step is where you're adding hot water to the mash, so this is the temperature of the water - * being added. + * being added. This is not part of BeerJSON, so I guess the thinking there is that the temperature of the + * water being added is either not important or can be calculated from other data (including the difference + * between the start and end temperatures of the step). */ Q_PROPERTY(std::optional infuseTemp_c READ infuseTemp_c WRITE setInfuseTemp_c ) // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ diff --git a/src/model/NamedEntity.cpp b/src/model/NamedEntity.cpp index 379554ef6..ec31a1947 100644 --- a/src/model/NamedEntity.cpp +++ b/src/model/NamedEntity.cpp @@ -461,7 +461,7 @@ void NamedEntity::prepareForPropertyChange(BtStringConst const & propertyName) { void NamedEntity::propagatePropertyChange(BtStringConst const & propertyName, bool notify) const { if (!this->m_propagationAndSignalsEnabled) { - qDebug() << Q_FUNC_INFO << "m_propagationAndSignalsEnabled unset"; + qDebug() << Q_FUNC_INFO << "m_propagationAndSignalsEnabled unset on" << this->metaObject()->className(); return; } diff --git a/src/model/NamedEntity.h b/src/model/NamedEntity.h index 1fd48a6da..3e2066572 100644 --- a/src/model/NamedEntity.h +++ b/src/model/NamedEntity.h @@ -601,7 +601,7 @@ class NamedEntity : public QObject { if (newValue == memberVariable) { qDebug() << Q_FUNC_INFO << this->metaObject()->className() << "#" << this->key() << ": ignoring call to setter for" << - propertyName << "as value not changing"; + propertyName << "as value (" << newValue << ") not changing"; return true; } return false; @@ -618,6 +618,8 @@ class NamedEntity : public QObject { bool setAndNotify(BtStringConst const & propertyName, T & memberVariable, T const newValue) { + // Normally leave this log statement commented out as it generates too many lines in the log file +// qDebug() << Q_FUNC_INFO << propertyName << ": change from" << memberVariable << "to" << newValue; if (this->newValueMatchesExisting(propertyName, memberVariable, newValue)) { return false; } diff --git a/src/model/NamedParameterBundle.cpp b/src/model/NamedParameterBundle.cpp index e1c0962b8..a669313b8 100644 --- a/src/model/NamedParameterBundle.cpp +++ b/src/model/NamedParameterBundle.cpp @@ -93,9 +93,12 @@ void NamedParameterBundle::insertIfNotPresent(P const & propertyNameOrPath, QVar return; } -// Instantiate the above template for the two types we care about -template<> void NamedParameterBundle::insertIfNotPresent(BtStringConst const & propertyName, QVariant const & value); -template<> void NamedParameterBundle::insertIfNotPresent(PropertyPath const & propertyPath, QVariant const & value); +// +// Instantiate the above template functions for the types that are going to use them +// (This is all just a trick to allow the template definition to be here in the .cpp file and not in the header.) +// +template void NamedParameterBundle::insertIfNotPresent(BtStringConst const & propertyName, QVariant const & value); +template void NamedParameterBundle::insertIfNotPresent(PropertyPath const & propertyPath, QVariant const & value); std::size_t NamedParameterBundle::size() const noexcept { // @@ -165,13 +168,6 @@ NamedParameterBundle const & NamedParameterBundle::getBundle(BtStringConst const return this->m_containedBundles.at(*propertyName); } - -///template -///S & operator<<(S & stream, NamedParameterBundle const & namedParameterBundle); -/// -///template -///S & operator<<(S & stream, NamedParameterBundle const * namedParameterBundle); - template S & NamedParameterBundle::writeToStream(S & stream, QString const indent) const { stream << indent << this->size() << "element NamedParameterBundle @" << diff --git a/src/model/OutlineableNamedEntity.h b/src/model/OutlineableNamedEntity.h index 56fec0380..b6bf15a6f 100755 --- a/src/model/OutlineableNamedEntity.h +++ b/src/model/OutlineableNamedEntity.h @@ -19,14 +19,14 @@ #include "model/NamedEntity.h" -//╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ +//====================================================================================================================== //========================================== Start of property name constants ========================================== // See comment in model/NamedEntity.h #define AddPropertyName(property) namespace PropertyNames::OutlineableNamedEntity { BtStringConst const property{#property}; } AddPropertyName(outline) #undef AddPropertyName //=========================================== End of property name constants =========================================== -//╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ +//====================================================================================================================== /** @@ -51,7 +51,7 @@ AddPropertyName(outline) * │ │ • yield † │ • alpha_acid │ │ │ • iron │ * │ │ • color │ • beta_acid │ │ │ • nitrate │ * │ │ │ │ │ │ • nitrite │ - * │ │ │ │ │ │ • flouride │ + * │ │ │ │ │ │ • flouride ⹋ │ * │ │ │ │ │ │ • sulfate │ * │ │ │ │ │ │ • chloride │ * │ │ │ │ │ │ • sodium │ @@ -79,7 +79,7 @@ AddPropertyName(outline) * │ │ • fermentability │ │ │ │ │ * │ │ • beta_glucan │ │ │ │ │ * └────────────┴───────────────────┴─────────────────┴──────────────┴───────────────────────┴───────────────┘ - * † = compound field; ‡ = field we don't support + * † = compound field; ‡ = field we don't support; ⹋ sic -- see https://github.com/beerjson/beerjson/issues/214 * * What this means is that, when we read in a Hop/Fermentable/Misc/Yeast/Water inside a RecipeAddition/ * RecipeUseOf record (an XxxxAdditionType record in BeerJSON) then we need different logic when we "check for diff --git a/src/model/Water.cpp b/src/model/Water.cpp index 1d83311c0..620fc467f 100644 --- a/src/model/Water.cpp +++ b/src/model/Water.cpp @@ -69,7 +69,7 @@ bool Water::isEqualTo(NamedEntity const & other) const { Utils::AutoCompare(this->m_iron_ppm , rhs.m_iron_ppm ) && Utils::AutoCompare(this->m_nitrate_ppm , rhs.m_nitrate_ppm ) && Utils::AutoCompare(this->m_nitrite_ppm , rhs.m_nitrite_ppm ) && - Utils::AutoCompare(this->m_flouride_ppm , rhs.m_flouride_ppm ) && + Utils::AutoCompare(this->m_fluoride_ppm , rhs.m_fluoride_ppm ) && Utils::AutoCompare(this->m_sulfate_ppm , rhs.m_sulfate_ppm ) && Utils::AutoCompare(this->m_chloride_ppm , rhs.m_chloride_ppm ) && Utils::AutoCompare(this->m_sodium_ppm , rhs.m_sodium_ppm ) && @@ -117,7 +117,7 @@ TypeLookup const Water::typeLookup { PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::iron_ppm , Water::m_iron_ppm , Measurement::PhysicalQuantity::MassFractionOrConc), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::nitrate_ppm , Water::m_nitrate_ppm , Measurement::PhysicalQuantity::MassFractionOrConc), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::nitrite_ppm , Water::m_nitrite_ppm , Measurement::PhysicalQuantity::MassFractionOrConc), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::flouride_ppm , Water::m_flouride_ppm , Measurement::PhysicalQuantity::MassFractionOrConc), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::fluoride_ppm , Water::m_fluoride_ppm , Measurement::PhysicalQuantity::MassFractionOrConc), }, // Parent classes lookup {&OutlineableNamedEntity::typeLookup, @@ -148,7 +148,7 @@ Water::Water(QString name) : m_iron_ppm {std::nullopt}, m_nitrate_ppm {std::nullopt}, m_nitrite_ppm {std::nullopt}, - m_flouride_ppm {std::nullopt} { + m_fluoride_ppm {std::nullopt} { CONSTRUCTOR_END return; @@ -177,7 +177,7 @@ Water::Water(NamedParameterBundle const & namedParameterBundle) : SET_REGULAR_FROM_NPB (m_iron_ppm , namedParameterBundle, PropertyNames::Water::iron_ppm , std::nullopt), SET_REGULAR_FROM_NPB (m_nitrate_ppm , namedParameterBundle, PropertyNames::Water::nitrate_ppm , std::nullopt), SET_REGULAR_FROM_NPB (m_nitrite_ppm , namedParameterBundle, PropertyNames::Water::nitrite_ppm , std::nullopt), - SET_REGULAR_FROM_NPB (m_flouride_ppm , namedParameterBundle, PropertyNames::Water::flouride_ppm , std::nullopt) { + SET_REGULAR_FROM_NPB (m_fluoride_ppm , namedParameterBundle, PropertyNames::Water::fluoride_ppm , std::nullopt) { CONSTRUCTOR_END return; @@ -206,7 +206,7 @@ Water::Water(Water const& other) : m_iron_ppm {other.m_iron_ppm }, m_nitrate_ppm {other.m_nitrate_ppm }, m_nitrite_ppm {other.m_nitrite_ppm }, - m_flouride_ppm {other.m_flouride_ppm } { + m_fluoride_ppm {other.m_fluoride_ppm } { CONSTRUCTOR_END return; @@ -237,7 +237,7 @@ void Water::swap(NamedEntity & other) noexcept { std::swap(this->m_iron_ppm , otherWater.m_iron_ppm ); std::swap(this->m_nitrate_ppm , otherWater.m_nitrate_ppm ); std::swap(this->m_nitrite_ppm , otherWater.m_nitrite_ppm ); - std::swap(this->m_flouride_ppm , otherWater.m_flouride_ppm ); + std::swap(this->m_fluoride_ppm , otherWater.m_fluoride_ppm ); return; } @@ -289,7 +289,7 @@ Water & Water::operator=(Water other) { if (this->m_iron_ppm != other.m_iron_ppm ) { this->propagatePropertyChange(PropertyNames::Water::iron_ppm ); } if (this->m_nitrate_ppm != other.m_nitrate_ppm ) { this->propagatePropertyChange(PropertyNames::Water::nitrate_ppm ); } if (this->m_nitrite_ppm != other.m_nitrite_ppm ) { this->propagatePropertyChange(PropertyNames::Water::nitrite_ppm ); } - if (this->m_flouride_ppm != other.m_flouride_ppm ) { this->propagatePropertyChange(PropertyNames::Water::flouride_ppm ); } + if (this->m_fluoride_ppm != other.m_fluoride_ppm ) { this->propagatePropertyChange(PropertyNames::Water::fluoride_ppm ); } return *this; } @@ -316,7 +316,7 @@ std::optional Water::potassium_ppm () const { return std::optional Water::iron_ppm () const { return m_iron_ppm ; } std::optional Water::nitrate_ppm () const { return m_nitrate_ppm ; } std::optional Water::nitrite_ppm () const { return m_nitrite_ppm ; } -std::optional Water::flouride_ppm () const { return m_flouride_ppm ; } +std::optional Water::fluoride_ppm () const { return m_fluoride_ppm ; } //============================================= "SETTER" MEMBER FUNCTIONS ============================================== ///void Water::setAmount (double const val) { SET_AND_NOTIFY(PropertyNames::Water::amount , m_amount , val ); return; } @@ -340,7 +340,7 @@ void Water::setPotassium_ppm (std::optional const val) { SET_AND_NOTIF void Water::setIron_ppm (std::optional const val) { SET_AND_NOTIFY(PropertyNames::Water::iron_ppm , m_iron_ppm , val); return; } void Water::setNitrate_ppm (std::optional const val) { SET_AND_NOTIFY(PropertyNames::Water::nitrate_ppm , m_nitrate_ppm , val); return; } void Water::setNitrite_ppm (std::optional const val) { SET_AND_NOTIFY(PropertyNames::Water::nitrite_ppm , m_nitrite_ppm , val); return; } -void Water::setFlouride_ppm (std::optional const val) { SET_AND_NOTIFY(PropertyNames::Water::flouride_ppm , m_flouride_ppm , val); return; } +void Water::setFluoride_ppm (std::optional const val) { SET_AND_NOTIFY(PropertyNames::Water::fluoride_ppm , m_fluoride_ppm , val); return; } double Water::ppm(Water::Ion const ion) const { switch (ion) { diff --git a/src/model/Water.h b/src/model/Water.h index 665aca15a..8bfa3054c 100644 --- a/src/model/Water.h +++ b/src/model/Water.h @@ -40,7 +40,7 @@ AddPropertyName(bicarbonate_ppm ) AddPropertyName(calcium_ppm ) AddPropertyName(carbonate_ppm ) AddPropertyName(chloride_ppm ) -AddPropertyName(flouride_ppm ) +AddPropertyName(fluoride_ppm ) AddPropertyName(iron_ppm ) AddPropertyName(magnesium_ppm ) AddPropertyName(mashRo_pct ) @@ -195,7 +195,7 @@ class Water : public OutlineableNamedEntity, Q_PROPERTY(std::optional iron_ppm READ iron_ppm WRITE setIron_ppm ) Q_PROPERTY(std::optional nitrate_ppm READ nitrate_ppm WRITE setNitrate_ppm ) Q_PROPERTY(std::optional nitrite_ppm READ nitrite_ppm WRITE setNitrite_ppm ) - Q_PROPERTY(std::optional flouride_ppm READ flouride_ppm WRITE setFlouride_ppm ) + Q_PROPERTY(std::optional fluoride_ppm READ fluoride_ppm WRITE setFluoride_ppm ) //============================================ "GETTER" MEMBER FUNCTIONS ============================================ /// double amount () const; @@ -219,7 +219,7 @@ class Water : public OutlineableNamedEntity, std::optional iron_ppm () const; std::optional nitrate_ppm () const; std::optional nitrite_ppm () const; - std::optional flouride_ppm () const; + std::optional fluoride_ppm () const; double ppm(Water::Ion const ion) const; @@ -246,7 +246,7 @@ class Water : public OutlineableNamedEntity, void setIron_ppm (std::optional const val); void setNitrate_ppm (std::optional const val); void setNitrite_ppm (std::optional const val); - void setFlouride_ppm (std::optional const val); + void setFluoride_ppm (std::optional const val); /// virtual Recipe * getOwningRecipe() const; @@ -277,7 +277,7 @@ class Water : public OutlineableNamedEntity, std::optional m_iron_ppm ; std::optional m_nitrate_ppm ; std::optional m_nitrite_ppm ; - std::optional m_flouride_ppm ; + std::optional m_fluoride_ppm ; }; BT_DECLARE_METATYPES(Water) diff --git a/src/serialization/json/BeerJson.cpp b/src/serialization/json/BeerJson.cpp index 9ea37aefa..2fd514b4b 100755 --- a/src/serialization/json/BeerJson.cpp +++ b/src/serialization/json/BeerJson.cpp @@ -503,7 +503,8 @@ namespace { {JsonRecordDefinition::FieldType::MeasurementWithUnits, "iron" , PropertyNames::Water::iron_ppm , &BEER_JSON_MASS_FRACT_OR_CONC_UNIT_MAPPER}, {JsonRecordDefinition::FieldType::MeasurementWithUnits, "nitrate" , PropertyNames::Water::nitrate_ppm , &BEER_JSON_MASS_FRACT_OR_CONC_UNIT_MAPPER}, {JsonRecordDefinition::FieldType::MeasurementWithUnits, "nitrite" , PropertyNames::Water::nitrite_ppm , &BEER_JSON_MASS_FRACT_OR_CONC_UNIT_MAPPER}, - {JsonRecordDefinition::FieldType::MeasurementWithUnits, "flouride" , PropertyNames::Water::flouride_ppm , &BEER_JSON_MASS_FRACT_OR_CONC_UNIT_MAPPER}, + // BeerJSON attribute is flouride but should be fluoride -- see https://github.com/beerjson/beerjson/issues/214 + {JsonRecordDefinition::FieldType::MeasurementWithUnits, "flouride" , PropertyNames::Water::fluoride_ppm , &BEER_JSON_MASS_FRACT_OR_CONC_UNIT_MAPPER}, {JsonRecordDefinition::FieldType::MeasurementWithUnits, "sulfate" , PropertyNames::Water::sulfate_ppm , &BEER_JSON_MASS_FRACT_OR_CONC_UNIT_MAPPER}, {JsonRecordDefinition::FieldType::MeasurementWithUnits, "chloride" , PropertyNames::Water::chloride_ppm , &BEER_JSON_MASS_FRACT_OR_CONC_UNIT_MAPPER}, {JsonRecordDefinition::FieldType::MeasurementWithUnits, "sodium" , PropertyNames::Water::sodium_ppm , &BEER_JSON_MASS_FRACT_OR_CONC_UNIT_MAPPER}, diff --git a/src/serialization/xml/BeerXml.cpp b/src/serialization/xml/BeerXml.cpp index 6ed0ba812..4493d244a 100755 --- a/src/serialization/xml/BeerXml.cpp +++ b/src/serialization/xml/BeerXml.cpp @@ -456,7 +456,7 @@ namespace { {XmlRecordDefinition::FieldType::Double , "IRON_PPM" , PropertyNames::Water::iron_ppm }, {XmlRecordDefinition::FieldType::Double , "NITRATE_PPM" , PropertyNames::Water::nitrate_ppm }, {XmlRecordDefinition::FieldType::Double , "NITRITE_PPM" , PropertyNames::Water::nitrite_ppm }, - {XmlRecordDefinition::FieldType::Double , "FLOURIDE_PPM" , PropertyNames::Water::flouride_ppm }, + {XmlRecordDefinition::FieldType::Double , "FLUORIDE_PPM" , PropertyNames::Water::fluoride_ppm }, }; std::initializer_list const BeerXml_WaterType_ExclBase { // Type XPath Q_PROPERTY Value Decoder diff --git a/src/tableModels/ItemDelegate.h b/src/tableModels/ItemDelegate.h index cc3731673..c1c09768d 100755 --- a/src/tableModels/ItemDelegate.h +++ b/src/tableModels/ItemDelegate.h @@ -263,13 +263,13 @@ class ItemDelegate { if (fieldType == NonPhysicalQuantity::Enum) { BtComboBox * comboBox = qobject_cast(editor); - model->setData(index, comboBox->getValue(typeInfo), Qt::EditRole); + model->setData(index, comboBox->getAsVariant(), Qt::EditRole); return; } if (fieldType == NonPhysicalQuantity::Bool) { BtBoolComboBox * boolComboBox = qobject_cast(editor); - model->setData(index, boolComboBox->getValue(typeInfo), Qt::EditRole); + model->setData(index, boolComboBox->getAsVariant(), Qt::EditRole); return; } @@ -295,7 +295,7 @@ class ItemDelegate { // Selector for editable amount that can be more than one physical quantity. (See comment above in // getEditWidget() for more details.) BtComboBox * comboBox = qobject_cast(editor); - model->setData(index, comboBox->getValue(typeInfo), Qt::EditRole); + model->setData(index, comboBox->getAsVariant(), Qt::EditRole); return; } diff --git a/src/trees/TreeView.cpp b/src/trees/TreeView.cpp index e6c82db1b..9c918c562 100644 --- a/src/trees/TreeView.cpp +++ b/src/trees/TreeView.cpp @@ -325,7 +325,7 @@ void TreeView::newNamedEntity() { if (m_type.testFlag(TreeModel::TypeMask::Misc )) { qobject_cast(m_editor)->newEditItem(folder); return; } if (m_type.testFlag(TreeModel::TypeMask::Style )) { qobject_cast(m_editor)->newEditItem(folder); return; } if (m_type.testFlag(TreeModel::TypeMask::Yeast )) { qobject_cast(m_editor)->newEditItem(folder); return; } - if (m_type.testFlag(TreeModel::TypeMask::Water )) { qobject_cast(m_editor)->newWater (folder); return; } + if (m_type.testFlag(TreeModel::TypeMask::Water )) { qobject_cast(m_editor)->newEditItem(folder); return; } qWarning() << Q_FUNC_INFO << "Unrecognized mask" << m_type; return; @@ -743,42 +743,11 @@ void TreeView::versionedRecipe(Recipe * descendant) { emit recipeSpawn(descendant); } -// Bad form likely - -RecipeTreeView::RecipeTreeView(QWidget * parent) : TreeView(parent, TreeModel::TypeMask::Recipe) { - connect(m_model, &TreeModel::recipeSpawn, this, &TreeView::versionedRecipe); -} - -EquipmentTreeView::EquipmentTreeView(QWidget * parent) - : TreeView(parent, TreeModel::TypeMask::Equipment) { -} - -// Icky ick ikcy -FermentableTreeView::FermentableTreeView(QWidget * parent) - : TreeView(parent, TreeModel::TypeMask::Fermentable) { -} - -// More Ick -HopTreeView::HopTreeView(QWidget * parent) - : TreeView(parent, TreeModel::TypeMask::Hop) { -} - -// Ick some more -MiscTreeView::MiscTreeView(QWidget * parent) - : TreeView(parent, TreeModel::TypeMask::Misc) { -} - -// Will this ick never end? -YeastTreeView::YeastTreeView(QWidget * parent) - : TreeView(parent, TreeModel::TypeMask::Yeast) { -} - -// Nope. Apparently not, cause I keep adding more -StyleTreeView::StyleTreeView(QWidget * parent) - : TreeView(parent, TreeModel::TypeMask::Style) { -} - -// Cthulhu take me -WaterTreeView::WaterTreeView(QWidget * parent) - : TreeView(parent, TreeModel::TypeMask::Water) { -} +TREE_VIEW_COMMON_CODE(Recipe) +TREE_VIEW_COMMON_CODE(Equipment) +TREE_VIEW_COMMON_CODE(Fermentable) +TREE_VIEW_COMMON_CODE(Hop) +TREE_VIEW_COMMON_CODE(Misc) +TREE_VIEW_COMMON_CODE(Yeast) +TREE_VIEW_COMMON_CODE(Style) +TREE_VIEW_COMMON_CODE(Water) diff --git a/src/trees/TreeView.h b/src/trees/TreeView.h index b1ef252bf..84d5ad0d6 100644 --- a/src/trees/TreeView.h +++ b/src/trees/TreeView.h @@ -26,6 +26,7 @@ #include "trees/TreeNode.h" #include "trees/TreeFilterProxyModel.h" +#include "utils/CuriouslyRecurringTemplateBase.h" // Forward declarations. class TreeModel; @@ -33,6 +34,7 @@ class Recipe; class Equipment; class Fermentable; class Hop; +class Mash; class Misc; class Yeast; class BrewNote; @@ -138,10 +140,10 @@ class TreeView : public QTreeView { public slots: void newNamedEntity(); + void versionedRecipe(Recipe * descendant); private slots: void expandFolder(TreeModel::TypeMasks kindaThing, QModelIndex fIdx); - void versionedRecipe(Recipe * descendant); void showAncestors(); void hideAncestors(); @@ -176,98 +178,61 @@ private slots: QString verifyCopy(QString tag, QString name, bool * abort); QMimeData * mimeData(QModelIndexList indexes); }; - -//! -// \class RecipeTreeView -// \brief subclasses TreeView to only show recipes. -class RecipeTreeView : public TreeView { - Q_OBJECT -public: - //! \brief Constructs the tree view, sets up the filter proxy and sets a - // few options on the tree that can only be set after the model - RecipeTreeView(QWidget * parent = nullptr); - -}; - -//! -// \class EquipmentTreeView -// \brief subclasses TreeView to only show equipment. -class EquipmentTreeView : public TreeView { - Q_OBJECT -public: - //! \brief Constructs the tree view, sets up the filter proxy and sets a - // few options on the tree that can only be set after the model - EquipmentTreeView(QWidget * parent = nullptr); -}; - -//! -// \class FermentableTreeView -// \brief subclasses TreeView to only show fermentables. -class FermentableTreeView : public TreeView { - Q_OBJECT -public: - //! \brief Constructs the tree view, sets up the filter proxy and sets a - // few options on the tree that can only be set after the model - FermentableTreeView(QWidget * parent = nullptr); - -}; - -//! -// \class HopTreeView -// \brief subclasses TreeView to only show hops. -class HopTreeView : public TreeView { - Q_OBJECT -public: - //! \brief Constructs the tree view, sets up the filter proxy and sets a - // few options on the tree that can only be set after the model - HopTreeView(QWidget * parent = nullptr); - -}; - -//! -// \class MiscTreeView -// \brief subclasses TreeView to only show miscs. -class MiscTreeView : public TreeView { - Q_OBJECT +//====================================================================================================================== +template class TreeViewPhantom; +template +class TreeViewBase : public CuriouslyRecurringTemplateBase { public: - //! \brief Constructs the tree view, sets up the filter proxy and sets a - // few options on the tree that can only be set after the model - MiscTreeView(QWidget * parent = nullptr); -}; + TreeViewBase() { + return; + } + ~TreeViewBase() = default; -//! -// \class YeastTreeView -// \brief subclasses TreeView to only show yeasts. -class YeastTreeView : public TreeView { - Q_OBJECT -public: - //! \brief Constructs the tree view, sets up the filter proxy and sets a - // few options on the tree that can only be set after the model - YeastTreeView(QWidget * parent = nullptr); + void doConnections() requires std::same_as { + this->derived().connect(this->derived().m_model, &TreeModel::recipeSpawn, &this->derived(), &TreeView::versionedRecipe); + return; + } -}; + void doConnections() requires (!std::same_as) { + return; + } -//! -// \class StyleTreeView -// \brief subclasses TreeView to only show styles. -class StyleTreeView : public TreeView { - Q_OBJECT -public: - //! \brief Constructs the tree view, sets up the filter proxy and sets a - // few options on the tree that can only be set after the model - StyleTreeView(QWidget * parent = nullptr); }; +//====================================================================================================================== +#define TREE_VIEW_COMMON_DECL(NeName) \ +class NeName##TreeView : public TreeView, \ + public TreeViewBase { \ + Q_OBJECT \ + \ + /* This allows TreeViewBase to call protected and private members of Derived */ \ + friend class TreeViewBase; \ + \ + public: \ + /* Constructs the tree view, sets up the filter proxy and sets a */ \ + /* few options on the tree that can only be set after the model */ \ + NeName##TreeView(QWidget * parent = nullptr); \ + virtual ~NeName##TreeView(); \ +}; \ + +TREE_VIEW_COMMON_DECL(Recipe) +TREE_VIEW_COMMON_DECL(Equipment) +TREE_VIEW_COMMON_DECL(Fermentable) +TREE_VIEW_COMMON_DECL(Hop) +TREE_VIEW_COMMON_DECL(Misc) +TREE_VIEW_COMMON_DECL(Yeast) +TREE_VIEW_COMMON_DECL(Style) +TREE_VIEW_COMMON_DECL(Water) + +#define TREE_VIEW_COMMON_CODE(NeName) \ + NeName##TreeView::NeName##TreeView(QWidget * parent) : \ + TreeView(parent, TreeModel::TypeMask::NeName), \ + TreeViewBase() { \ + this->doConnections(); \ + return; \ + } \ + \ + NeName##TreeView::~NeName##TreeView() = default; \ -//! -// \class WaterTreeView -// \brief subclasses TreeView to only show waters. -class WaterTreeView : public TreeView { - Q_OBJECT -public: - //! \brief Constructs the tree view, sets up the filter proxy and sets a - // few options on the tree that can only be set after the model - WaterTreeView(QWidget * parent = nullptr); -}; #endif diff --git a/src/utils/TypeLookup.cpp b/src/utils/TypeLookup.cpp index 36f58ec30..54e5b265a 100644 --- a/src/utils/TypeLookup.cpp +++ b/src/utils/TypeLookup.cpp @@ -56,6 +56,8 @@ TypeLookup::TypeLookup(char const * const } TypeInfo const * TypeLookup::typeInfoFor(BtStringConst const & propertyName) const { + // Normally keep this log statement commented out otherwise it generates too many lines in the log file +// qDebug() << Q_FUNC_INFO << this << "Searching for" << *propertyName; auto match = std::find_if( this->m_lookupMap.begin(), this->m_lookupMap.end(), diff --git a/src/widgets/BtBoolComboBox.cpp b/src/widgets/BtBoolComboBox.cpp index eb2f3a900..0fab48961 100755 --- a/src/widgets/BtBoolComboBox.cpp +++ b/src/widgets/BtBoolComboBox.cpp @@ -67,7 +67,8 @@ void BtBoolComboBox::init(char const * const editorName , QString const & unsetDisplay , QString const & setDisplay , TypeInfo const & typeInfo ) { - qDebug() << Q_FUNC_INFO << comboBoxFqName << ":" << typeInfo; + // Normally keep this log statement commented out otherwise it generates too many lines in the log file +// qDebug() << Q_FUNC_INFO << comboBoxFqName << ":" << typeInfo; // It's a coding error to call init twice Q_ASSERT(!this->pimpl->m_initialised); @@ -128,6 +129,21 @@ void BtBoolComboBox::setNull() { return; } +void BtBoolComboBox::setDefault() { + this->setCurrentIndex(0); + return; +} + +void BtBoolComboBox::setFromVariant(QVariant const & value) { + Q_ASSERT(this->pimpl->m_initialised); + if (this->pimpl->m_typeInfo->isOptional()) { + this->setValue(value.value>()); + } else { + this->setValue(value.value()); + } + return; +} + [[nodiscard]] bool BtBoolComboBox::getNonOptBoolValue() const { Q_ASSERT(!this->isOptional()); QString const rawValue = this->currentData().toString(); @@ -146,8 +162,8 @@ void BtBoolComboBox::setNull() { return rawValue == trueValue; } -[[nodiscard]] QVariant BtBoolComboBox::getValue(TypeInfo const & typeInfo) const { - if (typeInfo.isOptional()) { +[[nodiscard]] QVariant BtBoolComboBox::getAsVariant() const { + if (this->pimpl->m_typeInfo->isOptional()) { return QVariant::fromValue(this->getOptBoolValue()); } return QVariant::fromValue(this->getNonOptBoolValue()); diff --git a/src/widgets/BtBoolComboBox.h b/src/widgets/BtBoolComboBox.h index 46038e259..ec6590987 100755 --- a/src/widgets/BtBoolComboBox.h +++ b/src/widgets/BtBoolComboBox.h @@ -85,6 +85,13 @@ Q_OBJECT void setNull(); + void setDefault(); + + /** + * \brief Similar to \c SmartField::setFromVariant + */ + void setFromVariant(QVariant const & value); + /** * \brief Get value of a combo box for a non-optional bool */ @@ -96,9 +103,9 @@ Q_OBJECT [[nodiscard]] std::optional getOptBoolValue() const; /** - * \brief Get value of a combo box, using \c typeInfo to determine whether it is optional or not + * \brief Similar to \c SmartField::getAsVariant */ - [[nodiscard]] QVariant getValue(TypeInfo const & typeInfo) const; + [[nodiscard]] QVariant getAsVariant() const; private: // Private implementation details - see https://herbsutter.com/gotw/_100/ diff --git a/src/widgets/BtComboBox.cpp b/src/widgets/BtComboBox.cpp index 4f14cda28..85a4cb94e 100755 --- a/src/widgets/BtComboBox.cpp +++ b/src/widgets/BtComboBox.cpp @@ -116,6 +116,11 @@ void BtComboBox::init(char const * const editorName , } void BtComboBox::autoSetFromControlledField() { + // Normally keep this log statement commented out otherwise it generates too many lines in the log file +// qDebug().noquote() << +// Q_FUNC_INFO << this->pimpl->m_comboBoxFqName << ":" << this->pimpl->m_typeInfo->fieldType << +// Logging::getStackTrace(); + // It's a coding error to call this when there is no controlled field Q_ASSERT(this->pimpl->m_controlledField); @@ -153,8 +158,32 @@ void BtComboBox::setValue(int value) { return; } -QVariant BtComboBox::getValue(TypeInfo const & typeInfo) const { - if (typeInfo.isOptional()) { + +void BtComboBox::setDefault() { + this->setCurrentIndex(0); + return; +} + +void BtComboBox::setFromVariant(QVariant const & value) { + Q_ASSERT(this->pimpl->m_initialised); + // We assume the QVariant holds an int or an optional int, as we do for serialisation etc, as otherwise it gets hard + // to handle strongly-typed enums generically at runtime. + if (this->pimpl->m_typeInfo->isOptional()) { + auto vv {value.value>()}; + if (vv) { + this->setValue(*vv); + } else { + this->setNull(); + } + } else { + this->setValue(value.value()); + } + return; +} + +QVariant BtComboBox::getAsVariant() const { + Q_ASSERT(this->pimpl->m_initialised); + if (this->pimpl->m_typeInfo->isOptional()) { return QVariant::fromValue(this->getOptIntValue()); } return QVariant::fromValue(this->getNonOptIntValue()); diff --git a/src/widgets/BtComboBox.h b/src/widgets/BtComboBox.h index dd8624809..136ff21b1 100755 --- a/src/widgets/BtComboBox.h +++ b/src/widgets/BtComboBox.h @@ -138,9 +138,9 @@ Q_OBJECT } /** - * \brief Get value of a combo box, using \c typeInfo to determine whether it is optional or not + * \brief Get value of a combo box */ - [[nodiscard]] QVariant getValue(TypeInfo const & typeInfo) const; + [[nodiscard]] QVariant getAsVariant() const; /** * \brief Called from templated version of \c setValue, but also used in generic code (eg \c ItemDelegate) where we @@ -154,6 +154,13 @@ Q_OBJECT */ void setValue(int value); + void setDefault(); + + /** + * \brief Similar to \c SmartField::setFromVariant + */ + void setFromVariant(QVariant const & value); + [[nodiscard]] std::optional getOptIntValue() const; [[nodiscard]] int getNonOptIntValue() const; diff --git a/src/widgets/SmartField.cpp b/src/widgets/SmartField.cpp index 1c7a80ec2..ca916999d 100644 --- a/src/widgets/SmartField.cpp +++ b/src/widgets/SmartField.cpp @@ -358,12 +358,16 @@ void SmartField::setAmount(Measurement::Amount const & amount) { // For the moment, I'm going to say this function should _only_ be called for ChoiceOfPhysicalQuantity Q_ASSERT(std::holds_alternative(*this->getTypeInfo().fieldType)); + // Usually leave this debug log commented out unless trouble-shooting as it generates a lot of logging +// qDebug() << Q_FUNC_INFO << this->pimpl->m_fieldFqName << "amount:" << amount; this->setRawText(this->displayAmount(amount, this->pimpl->m_precision)); return; } void SmartField::setAmount(std::optional const & amount) { Q_ASSERT(this->pimpl->m_initialised); + // Usually leave this debug log commented out unless trouble-shooting as it generates a lot of logging +// qDebug() << Q_FUNC_INFO << this->pimpl->m_fieldFqName << "amount:" << amount; if (!amount) { this->setRawText(""); return; @@ -372,6 +376,12 @@ void SmartField::setAmount(std::optional const & amount) { return; } +void SmartField::setDefault() { + Q_ASSERT(this->pimpl->m_initialised); + this->setRawText(""); + return; +} + // Instantiate the above template functions for the types that are going to use it // (This is all just a trick to allow the template definition to be here in the .cpp file and not in the header, which // saves having to put a bunch of std::string stuff there.) @@ -418,9 +428,9 @@ void SmartField::setFromVariant(QVariant const & value) { } } else if (ti == typeid(Measurement::Amount)) { if (typeInfo.isOptional()) { - this->setAmount(value.value()); - } else { this->setAmount(value.value>()); + } else { + this->setAmount(value.value()); } } else if (ti == typeid(QString)) { Q_ASSERT(!typeInfo.isOptional()); diff --git a/src/widgets/SmartField.h b/src/widgets/SmartField.h index 432da643c..0710daf79 100644 --- a/src/widgets/SmartField.h +++ b/src/widgets/SmartField.h @@ -257,6 +257,12 @@ class SmartField : public SmartBase { void setAmount(std::optional const & amount); + /** + * \brief At the moment, this sets the field value to blank text, but we could imagine it being more sophisticated in + * future. + */ + void setDefault(); + /** * \brief Wrapper to call \c setAmount or correct version of \c setQuantity, from a \c QVariant parameter that would * typically have been obtained from Qt property system, eg via \c QObject::property(). diff --git a/translations/bt_ca.ts b/translations/bt_ca.ts index e6aebc5fa..f23b74c23 100644 --- a/translations/bt_ca.ts +++ b/translations/bt_ca.ts @@ -85,6 +85,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -2357,6 +2369,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -3963,6 +3983,38 @@ Log file may contain more details. ft + + No + No + + + Yes + + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -5810,11 +5862,11 @@ El volum final al primari és de %1. - Water name + CaCO3 - Water name: + HCO3 @@ -7069,10 +7121,6 @@ El volum final al primari és de %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -7185,6 +7233,10 @@ El volum final al primari és de %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -8733,7 +8785,7 @@ El volum final al primari és de %1. Time - Temps + Temps Amount @@ -10249,7 +10301,7 @@ El volum final al primari és de %1. Add to Secondary - Afegiu al secundari + Afegiu al secundari Checked means add this yeast to secondary instead of primary @@ -10335,10 +10387,6 @@ El volum final al primari és de %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_cs.ts b/translations/bt_cs.ts index 9df740ad6..38c1aa7e3 100644 --- a/translations/bt_cs.ts +++ b/translations/bt_cs.ts @@ -85,6 +85,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -2246,6 +2258,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -3836,6 +3856,38 @@ Log file may contain more details. ft + + No + Ne + + + Yes + Ano + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -5656,11 +5708,11 @@ Celkový objem pro hlavní kvašení je %1. - Water name + CaCO3 - Water name: + HCO3 @@ -6921,10 +6973,6 @@ Celkový objem pro hlavní kvašení je %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -7037,6 +7085,10 @@ Celkový objem pro hlavní kvašení je %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -8585,7 +8637,7 @@ Celkový objem pro hlavní kvašení je %1. Time - Čas + Čas Amount @@ -10085,7 +10137,7 @@ Celkový objem pro hlavní kvašení je %1. Add to Secondary - Přidat při dokvašování + Přidat při dokvašování Checked means add this yeast to secondary instead of primary @@ -10163,10 +10215,6 @@ Celkový objem pro hlavní kvašení je %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_de.ts b/translations/bt_de.ts index 0844e4933..9e033afa0 100644 --- a/translations/bt_de.ts +++ b/translations/bt_de.ts @@ -85,6 +85,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -2317,6 +2329,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -3911,6 +3931,38 @@ Log file may contain more details. ft + + No + Nein + + + Yes + Ja + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -5727,11 +5779,11 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. - Water name + CaCO3 - Water name: + HCO3 @@ -6958,10 +7010,6 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -7082,6 +7130,10 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -8626,7 +8678,7 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. Time - Zeit + Zeit Amount @@ -10118,7 +10170,7 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. Add to Secondary - Zur Nachgärung hinzufügen + Zur Nachgärung hinzufügen Checked means add this yeast to secondary instead of primary @@ -10204,10 +10256,6 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_el.ts b/translations/bt_el.ts index ff5fdadf1..c4c9840fb 100644 --- a/translations/bt_el.ts +++ b/translations/bt_el.ts @@ -85,6 +85,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -2270,6 +2282,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -3860,6 +3880,38 @@ Log file may contain more details. ft + + No + Όχι + + + Yes + Ναι + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -5692,11 +5744,11 @@ The final volume in the primary is %1. - Water name + CaCO3 - Water name: + HCO3 @@ -6951,10 +7003,6 @@ The final volume in the primary is %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -7071,6 +7119,10 @@ The final volume in the primary is %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -8619,7 +8671,7 @@ The final volume in the primary is %1. Time - Χρόνος + Χρόνος Amount @@ -10119,7 +10171,7 @@ The final volume in the primary is %1. Add to Secondary - Προσθήκη στο δεύτερο κάδο ζύμωσης + Προσθήκη στο δεύτερο κάδο ζύμωσης Checked means add this yeast to secondary instead of primary @@ -10205,10 +10257,6 @@ The final volume in the primary is %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_en.ts b/translations/bt_en.ts index d7c9f6aa1..4758696ea 100644 --- a/translations/bt_en.ts +++ b/translations/bt_en.ts @@ -81,6 +81,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -1409,6 +1421,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -2670,6 +2690,38 @@ Log file may contain more details. ft + + No + + + + Yes + + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -4388,11 +4440,11 @@ The final volume in the primary is %1. - Water name + CaCO3 - Water name: + HCO3 @@ -5351,10 +5403,6 @@ The final volume in the primary is %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -5463,6 +5511,10 @@ The final volume in the primary is %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -6697,10 +6749,6 @@ The final volume in the primary is %1. Type - - Time - - Amount in Inventory @@ -7897,10 +7945,6 @@ The final volume in the primary is %1. Max recultures - - Add to Secondary - - Main @@ -7961,10 +8005,6 @@ The final volume in the primary is %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_es.ts b/translations/bt_es.ts index f3d0641be..65120049f 100644 --- a/translations/bt_es.ts +++ b/translations/bt_es.ts @@ -85,6 +85,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -2341,6 +2353,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -3931,6 +3951,38 @@ Log file may contain more details. ft + + No + No + + + Yes + + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -5766,11 +5818,11 @@ El volumen final en el primario es %1. - Water name + CaCO3 - Water name: + HCO3 @@ -7021,10 +7073,6 @@ El volumen final en el primario es %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -7141,6 +7189,10 @@ El volumen final en el primario es %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -8685,7 +8737,7 @@ El volumen final en el primario es %1. Time - Tiempo + Tiempo Amount @@ -10185,7 +10237,7 @@ El volumen final en el primario es %1. Add to Secondary - Agregar al Secundario + Agregar al Secundario Checked means add this yeast to secondary instead of primary @@ -10263,10 +10315,6 @@ El volumen final en el primario es %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_et.ts b/translations/bt_et.ts index f77b74b0e..2636d5be9 100644 --- a/translations/bt_et.ts +++ b/translations/bt_et.ts @@ -81,6 +81,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -1482,6 +1494,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -2747,6 +2767,38 @@ Log file may contain more details. ft + + No + + + + Yes + + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -4473,11 +4525,11 @@ The final volume in the primary is %1. - Water name + CaCO3 - Water name: + HCO3 @@ -5447,10 +5499,6 @@ The final volume in the primary is %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -5559,6 +5607,10 @@ The final volume in the primary is %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -6803,7 +6855,7 @@ The final volume in the primary is %1. Time - Kestus + Kestus Amount in Inventory @@ -8005,10 +8057,6 @@ The final volume in the primary is %1. Max recultures - - Add to Secondary - - Main @@ -8069,10 +8117,6 @@ The final volume in the primary is %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_eu.ts b/translations/bt_eu.ts index ce74ff1e8..0e0b38ccc 100644 --- a/translations/bt_eu.ts +++ b/translations/bt_eu.ts @@ -81,6 +81,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -1490,6 +1502,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -2755,6 +2775,38 @@ Log file may contain more details. ft + + No + + + + Yes + + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -4485,11 +4537,11 @@ The final volume in the primary is %1. - Water name + CaCO3 - Water name: + HCO3 @@ -5459,10 +5511,6 @@ The final volume in the primary is %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -5571,6 +5619,10 @@ The final volume in the primary is %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -6815,7 +6867,7 @@ The final volume in the primary is %1. Time - Denbora + Denbora Amount in Inventory @@ -8017,10 +8069,6 @@ The final volume in the primary is %1. Max recultures - - Add to Secondary - - Main @@ -8081,10 +8129,6 @@ The final volume in the primary is %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_fr.ts b/translations/bt_fr.ts index 5739dd93c..e81657b6d 100644 --- a/translations/bt_fr.ts +++ b/translations/bt_fr.ts @@ -85,6 +85,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -2361,6 +2373,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -3967,6 +3987,38 @@ Log file may contain more details. ft + + No + Non + + + Yes + Oui + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -5806,11 +5858,11 @@ Le volume final dans la cuve de fermentation est de %1. - Water name + CaCO3 - Water name: + HCO3 @@ -7075,10 +7127,6 @@ Le volume final dans la cuve de fermentation est de %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -7199,6 +7247,10 @@ Le volume final dans la cuve de fermentation est de %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -8751,7 +8803,7 @@ Le volume final dans la cuve de fermentation est de %1. Time - Durée + Durée Amount @@ -10267,7 +10319,7 @@ Le volume final dans la cuve de fermentation est de %1. Add to Secondary - Ajouter en secondaire + Ajouter en secondaire Checked means add this yeast to secondary instead of primary @@ -10353,10 +10405,6 @@ Le volume final dans la cuve de fermentation est de %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_gl.ts b/translations/bt_gl.ts index b97167cd6..51c522db9 100644 --- a/translations/bt_gl.ts +++ b/translations/bt_gl.ts @@ -81,6 +81,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -1762,6 +1774,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -3042,6 +3062,38 @@ Log file may contain more details. ft + + No + + + + Yes + + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -4812,11 +4864,11 @@ The final volume in the primary is %1. - Water name + CaCO3 - Water name: + HCO3 @@ -5801,10 +5853,6 @@ The final volume in the primary is %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -5913,6 +5961,10 @@ The final volume in the primary is %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -7177,7 +7229,7 @@ The final volume in the primary is %1. Time - Tempo + Tempo Amount in Inventory @@ -8395,10 +8447,6 @@ The final volume in the primary is %1. Max recultures - - Add to Secondary - - Main @@ -8459,10 +8507,6 @@ The final volume in the primary is %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_hu.ts b/translations/bt_hu.ts index 3fd78478d..932b3c6dc 100644 --- a/translations/bt_hu.ts +++ b/translations/bt_hu.ts @@ -85,6 +85,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -2333,6 +2345,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -3919,6 +3939,38 @@ Log file may contain more details. ft + + No + Nem + + + Yes + Igen + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -5738,11 +5790,11 @@ Végleges mennyiség az elsődleges erjesztőben: %1 - Water name + CaCO3 - Water name: + HCO3 @@ -6997,10 +7049,6 @@ Végleges mennyiség az elsődleges erjesztőben: %1 Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -7109,6 +7157,10 @@ Végleges mennyiség az elsődleges erjesztőben: %1 You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -8645,7 +8697,7 @@ Végleges mennyiség az elsődleges erjesztőben: %1 Time - Idõ + Idõ Amount @@ -9963,10 +10015,6 @@ Végleges mennyiség az elsődleges erjesztőben: %1 Max recultures - - Add to Secondary - - Notes: Megjegyzés: @@ -10035,10 +10083,6 @@ Végleges mennyiség az elsődleges erjesztőben: %1 Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_it.ts b/translations/bt_it.ts index b55897ddf..0e92439fc 100644 --- a/translations/bt_it.ts +++ b/translations/bt_it.ts @@ -85,6 +85,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -2357,6 +2369,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -3947,6 +3967,38 @@ Log file may contain more details. ft + + No + No + + + Yes + Si + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -5783,11 +5835,11 @@ Il Volume finale del primo è %1. - Water name + CaCO3 - Water name: + HCO3 @@ -7050,10 +7102,6 @@ Il Volume finale del primo è %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -7170,6 +7218,10 @@ Il Volume finale del primo è %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -8722,7 +8774,7 @@ Il Volume finale del primo è %1. Time - Tempo + Tempo Amount @@ -10240,7 +10292,7 @@ Il Volume finale del primo è %1. Add to Secondary - Aggiungi al secondario + Aggiungi al secondario Checked means add this yeast to secondary instead of primary @@ -10326,10 +10378,6 @@ Il Volume finale del primo è %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_lv.ts b/translations/bt_lv.ts index 4fb236e11..82b6bb85d 100644 --- a/translations/bt_lv.ts +++ b/translations/bt_lv.ts @@ -81,6 +81,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -1615,6 +1627,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -2895,6 +2915,38 @@ Log file may contain more details. ft + + No + + + + Yes + + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -4649,11 +4701,11 @@ The final volume in the primary is %1. - Water name + CaCO3 - Water name: + HCO3 @@ -5642,10 +5694,6 @@ The final volume in the primary is %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -5754,6 +5802,10 @@ The final volume in the primary is %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -7018,7 +7070,7 @@ The final volume in the primary is %1. Time - Laiks + Laiks Notes: @@ -8240,10 +8292,6 @@ The final volume in the primary is %1. Max recultures - - Add to Secondary - - Notes: Piezīmes: @@ -8312,10 +8360,6 @@ The final volume in the primary is %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_nb.ts b/translations/bt_nb.ts index 4082fa409..1473d82a1 100644 --- a/translations/bt_nb.ts +++ b/translations/bt_nb.ts @@ -85,6 +85,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -2266,6 +2278,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -3864,6 +3884,38 @@ Log file may contain more details. ft + + No + Nei + + + Yes + Ja + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -5699,11 +5751,11 @@ Sluttvolumet i primærgjæringskaret er %1. - Water name + CaCO3 - Water name: + HCO3 @@ -6962,10 +7014,6 @@ Sluttvolumet i primærgjæringskaret er %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -7082,6 +7130,10 @@ Sluttvolumet i primærgjæringskaret er %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -8630,7 +8682,7 @@ Sluttvolumet i primærgjæringskaret er %1. Time - Tid + Tid Amount @@ -10129,7 +10181,7 @@ Sluttvolumet i primærgjæringskaret er %1. Add to Secondary - Legg til i sekundær + Legg til i sekundær Checked means add this yeast to secondary instead of primary @@ -10215,10 +10267,6 @@ Sluttvolumet i primærgjæringskaret er %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_nl.ts b/translations/bt_nl.ts index 2f2bdc9a3..a41646f51 100644 --- a/translations/bt_nl.ts +++ b/translations/bt_nl.ts @@ -85,6 +85,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -2369,6 +2381,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -3957,6 +3977,38 @@ Log file may contain more details. ft + + No + Nee + + + Yes + Ja + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -5780,11 +5832,11 @@ Het uiteindelijke volume in de hoofdvergisting is %1. - Water name + CaCO3 - Water name: + HCO3 @@ -7035,10 +7087,6 @@ Het uiteindelijke volume in de hoofdvergisting is %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -7155,6 +7203,10 @@ Het uiteindelijke volume in de hoofdvergisting is %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -8659,7 +8711,7 @@ Het uiteindelijke volume in de hoofdvergisting is %1. Time - Tijd + Tijd Amount @@ -10079,7 +10131,7 @@ Het uiteindelijke volume in de hoofdvergisting is %1. Add to Secondary - Voeg toe aan navergisting + Voeg toe aan navergisting Checked means add this yeast to secondary instead of primary @@ -10165,10 +10217,6 @@ Het uiteindelijke volume in de hoofdvergisting is %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_pl.ts b/translations/bt_pl.ts index 6b4cd7419..b50544bbe 100644 --- a/translations/bt_pl.ts +++ b/translations/bt_pl.ts @@ -85,6 +85,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -2238,6 +2250,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -3804,6 +3824,38 @@ Log file may contain more details. ft + + No + Nie + + + Yes + Tak + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -5620,11 +5672,11 @@ Końcowa pojemność w fermentorze wyniesie %1. - Water name + CaCO3 - Water name: + HCO3 @@ -6863,10 +6915,6 @@ Końcowa pojemność w fermentorze wyniesie %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -6987,6 +7035,10 @@ Końcowa pojemność w fermentorze wyniesie %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -8535,7 +8587,7 @@ Końcowa pojemność w fermentorze wyniesie %1. Time - Czas + Czas Amount @@ -10035,7 +10087,7 @@ Końcowa pojemność w fermentorze wyniesie %1. Add to Secondary - Dodawane do fermentacji cichej + Dodawane do fermentacji cichej Checked means add this yeast to secondary instead of primary @@ -10121,10 +10173,6 @@ Końcowa pojemność w fermentorze wyniesie %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_pt.ts b/translations/bt_pt.ts index 8342de85d..09c52a43e 100644 --- a/translations/bt_pt.ts +++ b/translations/bt_pt.ts @@ -85,6 +85,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -2297,6 +2309,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -3895,6 +3915,38 @@ Log file may contain more details. ft + + No + Não + + + Yes + Sim + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -5726,11 +5778,11 @@ O volume final do fermentador primário é %1. - Water name + CaCO3 - Water name: + HCO3 @@ -6989,10 +7041,6 @@ O volume final do fermentador primário é %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -7109,6 +7157,10 @@ O volume final do fermentador primário é %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -8657,7 +8709,7 @@ O volume final do fermentador primário é %1. Time - Tempo + Tempo Amount @@ -10169,7 +10221,7 @@ O volume final do fermentador primário é %1. Add to Secondary - Adicionar ao Secundário + Adicionar ao Secundário Checked means add this yeast to secondary instead of primary @@ -10255,10 +10307,6 @@ O volume final do fermentador primário é %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_ru.ts b/translations/bt_ru.ts index f124c3b50..14c9733e7 100644 --- a/translations/bt_ru.ts +++ b/translations/bt_ru.ts @@ -85,6 +85,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -2333,6 +2345,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -3927,6 +3947,38 @@ Log file may contain more details. ft + + No + Нет + + + Yes + Да + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -5762,11 +5814,11 @@ The final volume in the primary is %1. - Water name + CaCO3 - Water name: + HCO3 @@ -7021,10 +7073,6 @@ The final volume in the primary is %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -7141,6 +7189,10 @@ The final volume in the primary is %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -8685,7 +8737,7 @@ The final volume in the primary is %1. Time - Время + Время Amount @@ -10177,7 +10229,7 @@ The final volume in the primary is %1. Add to Secondary - Добавлять на карбонизацию + Добавлять на карбонизацию Checked means add this yeast to secondary instead of primary @@ -10255,10 +10307,6 @@ The final volume in the primary is %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_sr.ts b/translations/bt_sr.ts index 89b202d7a..fdf24fc4a 100644 --- a/translations/bt_sr.ts +++ b/translations/bt_sr.ts @@ -81,6 +81,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -2106,6 +2118,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -3648,6 +3668,38 @@ Log file may contain more details. ft + + No + Не + + + Yes + Да + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -5441,11 +5493,11 @@ The final volume in the primary is %1. - Water name + CaCO3 - Water name: + HCO3 @@ -6521,10 +6573,6 @@ The final volume in the primary is %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -6633,6 +6681,10 @@ The final volume in the primary is %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -7997,7 +8049,7 @@ The final volume in the primary is %1. Time - Трајање + Трајање Amount @@ -9295,10 +9347,6 @@ The final volume in the primary is %1. Max recultures - - Add to Secondary - - Main @@ -9359,10 +9407,6 @@ The final volume in the primary is %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_sv.ts b/translations/bt_sv.ts index 8c097fd61..12dbffc77 100644 --- a/translations/bt_sv.ts +++ b/translations/bt_sv.ts @@ -85,6 +85,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -2361,6 +2373,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -3939,6 +3959,38 @@ Log file may contain more details. ft + + No + Nej + + + Yes + Ja + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -5767,11 +5819,11 @@ Primärens slutgiltiga volym är %1. - Water name + CaCO3 - Water name: + HCO3 @@ -7034,10 +7086,6 @@ Primärens slutgiltiga volym är %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -7158,6 +7206,10 @@ Primärens slutgiltiga volym är %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -8698,7 +8750,7 @@ Primärens slutgiltiga volym är %1. Time - Tid + Tid Amount @@ -10118,7 +10170,7 @@ Primärens slutgiltiga volym är %1. Add to Secondary - Tillsätt i sekundären + Tillsätt i sekundären Checked means add this yeast to secondary instead of primary @@ -10204,10 +10256,6 @@ Primärens slutgiltiga volym är %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_tr.ts b/translations/bt_tr.ts index 1a31d1bc5..37d81cae6 100644 --- a/translations/bt_tr.ts +++ b/translations/bt_tr.ts @@ -85,6 +85,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -2230,6 +2242,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -3894,6 +3914,38 @@ Günlük dosyası daha fazla detay içerebilir. ft + + No + Hayır + + + Yes + Evet + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -5782,11 +5834,19 @@ The final volume in the primary is %1. Water name - Su ismi + Su ismi Water name: - Su ismi: + Su ismi: + + + CaCO3 + + + + HCO3 + HCO3 @@ -6992,10 +7052,6 @@ The final volume in the primary is %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -7112,6 +7168,10 @@ The final volume in the primary is %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -8512,7 +8572,7 @@ The final volume in the primary is %1. Time - Süre + Süre Check it if the amount listed is in kg instead of L. @@ -9868,7 +9928,7 @@ The final volume in the primary is %1. Add to Secondary - İkincil Mayalayıcıya Ekle + İkincil Mayalayıcıya Ekle Checked means add this yeast to secondary instead of primary @@ -9942,10 +10002,6 @@ The final volume in the primary is %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/translations/bt_zh.ts b/translations/bt_zh.ts index b0d4a790e..b9021f343 100644 --- a/translations/bt_zh.ts +++ b/translations/bt_zh.ts @@ -85,6 +85,18 @@ Post-boil for %1 + + Automatically-generated pre-boil step for %1 + + + + Automatically-generated boil proper step for %1 + + + + Automatically-generated post-boil step for %1 + + BoilStep @@ -2158,6 +2170,14 @@ If you need help, please open an issue at %1 None of the selected items is exportable + + Automatically-created Boil for %1 + + + + Automatically-created Fermentation for %1 + + Mash @@ -3728,6 +3748,38 @@ Log file may contain more details. ft + + No + + + + Yes + + + + Boil for %1 + + + + Automatically created by BeerXML import + + + + Fermentation for %1 + + + + Primary Fermentation Step for %1 + + + + Secondary Fermentation Step for %1 + + + + Tertiary Fermentation Step for %1 + + RaIngrd @@ -5532,11 +5584,11 @@ The final volume in the primary is %1. - Water name + CaCO3 - Water name: + HCO3 @@ -6787,10 +6839,6 @@ The final volume in the primary is %1. Fermantable Type - - GrainGroup - - Fermantable Grain Group @@ -6911,6 +6959,10 @@ The final volume in the primary is %1. You can normally ignore this, but it's sometimes useful for debugging. + + Grain Group + + fermentationEditor @@ -8447,7 +8499,7 @@ The final volume in the primary is %1. Time - 时间Time + 时间Time Amount @@ -9943,7 +9995,7 @@ The final volume in the primary is %1. Add to Secondary - 加入二次Add to Secondary + 加入二次Add to Secondary Checked means add this yeast to secondary instead of primary @@ -10017,10 +10069,6 @@ The final volume in the primary is %1. Maximum apparent attenuation as percentage of OG points - - Whether to add this yeast to secondary instead of primary - - Alcohol Tolerance diff --git a/ui/fermentableEditor.ui b/ui/fermentableEditor.ui index 580bcee7c..6a65c49e9 100644 --- a/ui/fermentableEditor.ui +++ b/ui/fermentableEditor.ui @@ -69,6 +69,19 @@ + + + + Required + + + Type + + + comboBox_type + + + @@ -77,12 +90,6 @@ 0 - - - 100 - 16777215 - - Fermantable Type @@ -114,19 +121,6 @@ - - - - Required - - - Type - - - comboBox_type - - - @@ -189,12 +183,12 @@ - + Required - GrainGroup + Grain Group comboBox_grainGroup diff --git a/ui/fermentationEditor.ui b/ui/fermentationEditor.ui index e5aa1599c..fc7a5d363 100755 --- a/ui/fermentationEditor.ui +++ b/ui/fermentationEditor.ui @@ -56,7 +56,7 @@ - + Notes diff --git a/ui/hopEditor.ui b/ui/hopEditor.ui index 7adf2879f..3b81d7a00 100644 --- a/ui/hopEditor.ui +++ b/ui/hopEditor.ui @@ -814,7 +814,7 @@ - + Notes diff --git a/ui/miscEditor.ui b/ui/miscEditor.ui index 8e11f2fa2..4d161d5ca 100644 --- a/ui/miscEditor.ui +++ b/ui/miscEditor.ui @@ -129,29 +129,6 @@ - - - - Qt::CustomContextMenu - - - Time - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - lineEdit_time - - - - - - - Time - - - @@ -225,7 +202,7 @@ - + Use for @@ -345,7 +322,6 @@ tabWidget_editor lineEdit_name comboBox_type - lineEdit_time lineEdit_inventory pushButton_new pushButton_save diff --git a/ui/styleEditor.ui b/ui/styleEditor.ui index 8255c8e18..f52c8308f 100644 --- a/ui/styleEditor.ui +++ b/ui/styleEditor.ui @@ -36,7 +36,7 @@ Main - + Required @@ -49,14 +49,20 @@ - + + + + 10 + 0 + + Name - + Required @@ -69,21 +75,15 @@ - + Category - + - - - 0 - 0 - - Required @@ -95,21 +95,15 @@ - + Category number - + - - - 0 - 0 - - Required @@ -121,21 +115,15 @@ - + Style letter - + - - - 0 - 0 - - Required @@ -147,21 +135,15 @@ - + Style guide - + - - - 0 - 0 - - Required @@ -170,7 +152,7 @@ - + Type of beverage @@ -197,17 +179,17 @@ Ranges - - + + - Max + Min - - + + - Min + Max diff --git a/ui/waterEditor.ui b/ui/waterEditor.ui index 117b30739..b43ea65bb 100644 --- a/ui/waterEditor.ui +++ b/ui/waterEditor.ui @@ -46,7 +46,7 @@ QLayout::SetMinimumSize - + @@ -59,7 +59,7 @@ - + @@ -69,7 +69,7 @@ - + @@ -85,7 +85,7 @@ - + @@ -99,15 +99,22 @@ 0 - + + + + + + Qt::Horizontal + + - 16777215 - 16777215 + 40 + 20 - + - + @@ -123,7 +130,7 @@ - + @@ -137,15 +144,9 @@ 0 - - - 16777215 - 16777215 - - - + @@ -161,7 +162,7 @@ - + @@ -175,15 +176,9 @@ 0 - - - 16777215 - 16777215 - - - + @@ -202,7 +197,7 @@ - + @@ -216,15 +211,9 @@ 0 - - - 16777215 - 16777215 - - - + @@ -240,7 +229,7 @@ - + @@ -254,15 +243,9 @@ 0 - - - 16777215 - 16777215 - - - + @@ -281,7 +264,7 @@ - + @@ -295,15 +278,9 @@ 0 - - - 16777215 - 16777215 - - - + @@ -319,7 +296,7 @@ - + @@ -333,16 +310,10 @@ 0 - - - 16777215 - 16777215 - - - - + + 0 @@ -360,8 +331,8 @@ - - + + 0 @@ -380,50 +351,159 @@ - - - - - - + + + - Notes + Carbonate + + + lineEdit_carbonate - - - - - 0 - 0 - + + + + + + + + Potassium + + + lineEdit_potassium + + + + + + + + + + + Iron + + + lineEdit_iron + + + + + + + + + + + Flouride + + + lineEdit_fluoride + + + + + + + + + + + Nitrate + + + lineEdit_nitrate + + + + + + + + + + + Nitrite + + + lineEdit_nitrite + + + + + + + + Notes + + + + + + + + + + + + New hop + + + + + + + :/images/smallPlus.svg:/images/smallPlus.svg + + + + + + + Save and close + + + + + + + :/images/filesave.svg:/images/filesave.svg + + + + + + + Discard and close + + + + + + + :/images/exit.svg:/images/exit.svg + + + + + - - - - - 0 - 0 - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - + + BtBoolComboBox + QComboBox +
widgets/BtBoolComboBox.h
+
SmartLabel QLabel diff --git a/ui/yeastEditor.ui b/ui/yeastEditor.ui index bebe8a15e..7412c2d48 100644 --- a/ui/yeastEditor.ui +++ b/ui/yeastEditor.ui @@ -382,32 +382,6 @@
- - - - - 0 - 0 - - - - Add to Secondary - - - - - - - - 0 - 0 - - - - Whether to add this yeast to secondary instead of primary - - - @@ -538,7 +512,7 @@ - + Best For @@ -674,7 +648,6 @@ comboBox_yeastFlocculation lineEdit_maxReuse lineEdit_timesCultured - boolCombo_addToSecondary pushButton_new pushButton_save pushButton_cancel