From ca47646750456face0b7856a3cbc5437e83aab95 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Fri, 24 Mar 2023 08:55:24 +0100 Subject: [PATCH 01/42] More work on LineEdit, plus XML fix for optional --- src/CMakeLists.txt | 1 + src/database/ObjectStore.cpp | 16 +++--- src/measurement/Measurement.cpp | 1 + src/measurement/PhysicalQuantity.h | 1 + src/model/Hop.cpp | 33 ++++++------ src/model/NamedEntity.cpp | 1 - src/tableModels/MiscTableModel.cpp | 14 ++--- src/tableModels/YeastTableModel.cpp | 14 ++--- src/utils/TypeLookup.h | 1 - src/widgets/SmartLineEdit.cpp | 51 ++++++++++++++++++ src/widgets/SmartLineEdit.h | 82 +++++++++++++++++++++++++++++ src/xml/XmlRecord.cpp | 16 ++++-- 12 files changed, 185 insertions(+), 46 deletions(-) create mode 100644 src/widgets/SmartLineEdit.cpp create mode 100644 src/widgets/SmartLineEdit.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 19adbc74..4fbeaea7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -190,6 +190,7 @@ set(filesToCompile_cpp ${repoDir}/src/widgets/BtAmountDigitWidget.cpp ${repoDir}/src/widgets/BtDigitWidget.cpp ${repoDir}/src/widgets/SelectionControl.cpp + ${repoDir}/src/widgets/SmartLineEdit.cpp ${repoDir}/src/widgets/ToggleSwitch.cpp ${repoDir}/src/widgets/UnitAndScalePopUpMenu.cpp ${repoDir}/src/xml/BeerXml.cpp diff --git a/src/database/ObjectStore.cpp b/src/database/ObjectStore.cpp index 19a19474..cb7ce9bc 100644 --- a/src/database/ObjectStore.cpp +++ b/src/database/ObjectStore.cpp @@ -34,9 +34,6 @@ #include "model/NamedParameterBundle.h" #include "utils/OptionalHelpers.h" -//// DELETE THIS! -#include "model/Water.h" - // Private implementation details that don't need access to class member variables namespace { @@ -86,9 +83,9 @@ namespace { // ... // bug DATE // ); - // .:TBD:. At some future point we might extend our model to allow marking some columns as NOT NULL (eg via some - // making the derived class of NamedEntity available in ObjectStore::TableDefinition so we can query - // isOptional() on each property), but it doesn't seem pressing at the moment. + // .:TBD:. At some future point we might extend our model to allow marking some columns as NOT NULL (eg by making + // the derived class of NamedEntity available in ObjectStore::TableDefinition so we can query isOptional() + // on each property), but it doesn't seem pressing at the moment. // QString queryString{"CREATE TABLE "}; QTextStream queryStringAsStream{&queryString}; @@ -229,7 +226,6 @@ namespace { ObjectStore::TableField const & fieldDefn, QString const & stringValue) { // It's a coding error if we called this function for a non-enum field - // It's OK to be called for EnumOpt as stringToEnumOpt() calls us to avoid duplication Q_ASSERT(fieldDefn.fieldType == ObjectStore::FieldType::Enum); Q_ASSERT(fieldDefn.enumMapping != nullptr); auto match = fieldDefn.enumMapping->stringToEnumAsInt(stringValue); @@ -760,9 +756,9 @@ class ObjectStore::impl { if (propertyValue.isNull()) { // This is either a coding error or someone messed with the DB data. qCritical() << - Q_FUNC_INFO << "Found null value for non-optional enum when mapping column " << fieldDefn.columnName << - " to property " << fieldDefn.propertyName << "for" << primaryTable.tableName << - "so using 0"; + Q_FUNC_INFO << "Found null value for non-optional enum when mapping column " << + fieldDefn.columnName << " to property " << fieldDefn.propertyName << "for" << + primaryTable.tableName << "so using 0"; propertyValue = QVariant(0); return; } diff --git a/src/measurement/Measurement.cpp b/src/measurement/Measurement.cpp index e0f618cc..b3433368 100644 --- a/src/measurement/Measurement.cpp +++ b/src/measurement/Measurement.cpp @@ -24,6 +24,7 @@ #include "Algorithms.h" #include "Localization.h" +#include "measurement/PhysicalQuantity.h" #include "measurement/UnitSystem.h" #include "model/NamedEntity.h" #include "model/Style.h" // For PropertyNames::Style::colorMin_srm, PropertyNames::Style::colorMax_srm diff --git a/src/measurement/PhysicalQuantity.h b/src/measurement/PhysicalQuantity.h index c51e47ce..f1a4fbc0 100644 --- a/src/measurement/PhysicalQuantity.h +++ b/src/measurement/PhysicalQuantity.h @@ -17,6 +17,7 @@ #define MEASUREMENT_PHYSICALQUANTITY_H #pragma once +#include #include #include #include diff --git a/src/model/Hop.cpp b/src/model/Hop.cpp index f8eb6620..bacea70b 100644 --- a/src/model/Hop.cpp +++ b/src/model/Hop.cpp @@ -151,34 +151,35 @@ ObjectStore & Hop::getObjectStoreTypedInstance() const { TypeLookup const Hop::typeLookup { "Hop", { + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::use , Hop::m_use ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::type , Hop::m_type ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::form , Hop::m_form ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::alpha_pct , Hop::m_alpha_pct ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::amount_kg , Hop::m_amount_kg ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::time_min , Hop::m_time_min ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::notes , Hop::m_notes ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::beta_pct , Hop::m_beta_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::b_pinene_pct , Hop::m_b_pinene_pct ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::hsi_pct , Hop::m_hsi_pct ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::origin , Hop::m_origin ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::substitutes , Hop::m_substitutes ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::humulene_pct , Hop::m_humulene_pct ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::caryophyllene_pct , Hop::m_caryophyllene_pct ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::cohumulone_pct , Hop::m_cohumulone_pct ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::myrcene_pct , Hop::m_myrcene_pct ), + // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::producer , Hop::m_producer ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::product_id , Hop::m_product_id ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::year , Hop::m_year ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::total_oil_ml_per_100g, Hop::m_total_oil_ml_per_100g), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::farnesene_pct , Hop::m_farnesene_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::form , Hop::m_form ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::geraniol_pct , Hop::m_geraniol_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::hsi_pct , Hop::m_hsi_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::humulene_pct , Hop::m_humulene_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::limonene_pct , Hop::m_limonene_pct ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::b_pinene_pct , Hop::m_b_pinene_pct ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::linalool_pct , Hop::m_linalool_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::myrcene_pct , Hop::m_myrcene_pct ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::limonene_pct , Hop::m_limonene_pct ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::nerol_pct , Hop::m_nerol_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::notes , Hop::m_notes ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::origin , Hop::m_origin ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::pinene_pct , Hop::m_pinene_pct ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::polyphenols_pct , Hop::m_polyphenols_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::producer , Hop::m_producer ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::product_id , Hop::m_product_id ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::substitutes , Hop::m_substitutes ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::time_min , Hop::m_time_min ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::total_oil_ml_per_100g, Hop::m_total_oil_ml_per_100g), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::type , Hop::m_type ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::use , Hop::m_use ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::xanthohumol_pct , Hop::m_xanthohumol_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::year , Hop::m_year ), }, // Parent class lookup. NB: NamedEntityWithInventory not NamedEntity! &NamedEntityWithInventory::typeLookup diff --git a/src/model/NamedEntity.cpp b/src/model/NamedEntity.cpp index 4c07f262..75a37771 100644 --- a/src/model/NamedEntity.cpp +++ b/src/model/NamedEntity.cpp @@ -19,7 +19,6 @@ =====================================================================================================================*/ #include "model/NamedEntity.h" -#include #include #include diff --git a/src/tableModels/MiscTableModel.cpp b/src/tableModels/MiscTableModel.cpp index 2f28cd27..e8400c3a 100644 --- a/src/tableModels/MiscTableModel.cpp +++ b/src/tableModels/MiscTableModel.cpp @@ -41,13 +41,13 @@ MiscTableModel::MiscTableModel(QTableView* parent, bool editable) : BtTableModelInventory{ parent, editable, - {{MISCNAMECOL, {tr("Name"), NonPhysicalQuantity::String , "" }}, - {MISCTYPECOL, {tr("Type"), NonPhysicalQuantity::String , "" }}, - {MISCUSECOL, {tr("Use"), NonPhysicalQuantity::String , "" }}, - {MISCTIMECOL, {tr("Time"), Measurement::PhysicalQuantity::Time , *PropertyNames::Misc::time }}, - {MISCAMOUNTCOL, {tr("Amount"), Measurement::PqEitherMassOrVolumeConcentration, *PropertyNames::Misc::amount }}, - {MISCINVENTORYCOL, {tr("Inventory"), Measurement::PqEitherMassOrVolumeConcentration, *PropertyNames::NamedEntityWithInventory::inventory}}, - {MISCISWEIGHT, {tr("Amount Type"), NonPhysicalQuantity::String , "" }}} + {{MISCNAMECOL, {tr("Name"), NonPhysicalQuantity::String , "" }}, + {MISCTYPECOL, {tr("Type"), NonPhysicalQuantity::String , "" }}, + {MISCUSECOL, {tr("Use"), NonPhysicalQuantity::String , "" }}, + {MISCTIMECOL, {tr("Time"), Measurement::PhysicalQuantity::Time, *PropertyNames::Misc::time }}, + {MISCAMOUNTCOL, {tr("Amount"), Measurement::PqEitherMassOrVolume , *PropertyNames::Misc::amount }}, + {MISCINVENTORYCOL, {tr("Inventory"), Measurement::PqEitherMassOrVolume , *PropertyNames::NamedEntityWithInventory::inventory}}, + {MISCISWEIGHT, {tr("Amount Type"), NonPhysicalQuantity::String , "" }}} }, BtTableModelData{} { this->rows.clear(); diff --git a/src/tableModels/YeastTableModel.cpp b/src/tableModels/YeastTableModel.cpp index 4aaeec11..649ea9ab 100644 --- a/src/tableModels/YeastTableModel.cpp +++ b/src/tableModels/YeastTableModel.cpp @@ -49,13 +49,13 @@ YeastTableModel::YeastTableModel(QTableView * parent, bool editable) : BtTableModelInventory{ parent, editable, - {{YEASTNAMECOL, {tr("Name"), NonPhysicalQuantity::String , "" }}, - {YEASTLABCOL, {tr("Laboratory"), NonPhysicalQuantity::String , "" }}, - {YEASTPRODIDCOL, {tr("Product ID"), NonPhysicalQuantity::String , "" }}, - {YEASTTYPECOL, {tr("Type"), NonPhysicalQuantity::String , "" }}, - {YEASTFORMCOL, {tr("Form"), NonPhysicalQuantity::String , "" }}, - {YEASTAMOUNTCOL, {tr("Amount"), Measurement::PqEitherMassOrVolumeConcentration, *PropertyNames::Yeast::amount}}, - {YEASTINVENTORYCOL, {tr("Inventory"), NonPhysicalQuantity::Count , "" }}} + {{YEASTNAMECOL, {tr("Name"), NonPhysicalQuantity::String , "" }}, + {YEASTLABCOL, {tr("Laboratory"), NonPhysicalQuantity::String , "" }}, + {YEASTPRODIDCOL, {tr("Product ID"), NonPhysicalQuantity::String , "" }}, + {YEASTTYPECOL, {tr("Type"), NonPhysicalQuantity::String , "" }}, + {YEASTFORMCOL, {tr("Form"), NonPhysicalQuantity::String , "" }}, + {YEASTAMOUNTCOL, {tr("Amount"), Measurement::PqEitherMassOrVolume, *PropertyNames::Yeast::amount}}, + {YEASTINVENTORYCOL, {tr("Inventory"), NonPhysicalQuantity::Count , "" }}} }, BtTableModelData{} { diff --git a/src/utils/TypeLookup.h b/src/utils/TypeLookup.h index a7939c4a..e779172c 100644 --- a/src/utils/TypeLookup.h +++ b/src/utils/TypeLookup.h @@ -133,7 +133,6 @@ struct TypeInfo { class TypeLookup { public: - /** * \brief If we want to change from using std::map in future, it's easier if we have a typedef alias for it */ diff --git a/src/widgets/SmartLineEdit.cpp b/src/widgets/SmartLineEdit.cpp new file mode 100644 index 00000000..24ea9891 --- /dev/null +++ b/src/widgets/SmartLineEdit.cpp @@ -0,0 +1,51 @@ +/*====================================================================================================================== + * widgets/SmartLineEdit.cpp is part of Brewken, and is copyright the following authors 2009-2023: + * • Brian Rower + * • Mark de Wever + * • Mattias Måhl + * • Matt Young + * • Mike Evans + * • Mik Firestone + * • Philip Greggory Lee + * • Théophane Martin + * + * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see + * . + =====================================================================================================================*/ +#include "widgets/SmartLineEdit.h" + +// This private implementation class holds all private non-virtual members of SmartLineEdit +class SmartLineEdit::impl { +public: + impl(SmartLineEdit & self) : + self{self}, + initialised{false} { + return; + } + + ~impl() = default; + + SmartLineEdit & self; + bool initialised; +}; + +SmartLineEdit::SmartLineEdit(QWidget * parent) : QLineEdit(parent), pimpl{std::make_unique(*this)} { + return; +} + +SmartLineEdit::~SmartLineEdit() = default; + +void SmartLineEdit::configure(BtFieldType const fieldType, + int const defaultPrecision, + QString const & maximalDisplayString) { + // TODO Write this! + return; +} diff --git a/src/widgets/SmartLineEdit.h b/src/widgets/SmartLineEdit.h new file mode 100644 index 00000000..a08f5874 --- /dev/null +++ b/src/widgets/SmartLineEdit.h @@ -0,0 +1,82 @@ +/*====================================================================================================================== + * widgets/SmartLineEdit.h is part of Brewken, and is copyright the following authors 2009-2023: + * • Brian Rower + * • Mark de Wever + * • Matt Young + * • Mike Evans + * • Mik Firestone + * • Philip Greggory Lee + * • Scott Peshak + * + * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see + * . + =====================================================================================================================*/ +#ifndef WIDGETS_SMARTLINEEDIT_H +#define WIDGETS_SMARTLINEEDIT_H +#pragma once + +#include // For PImpl + +#include +#include +#include + +#include "BtFieldType.h" + +/*! + * \class SmartLineEdit + * + * \brief Extends QLineEdit to handle unit transformations and formatting + * + * A \c SmartLineEdit widget will usually have a corresponding \c SmartLabel. See comment in + * \c widgets/SmartLabel.h for more details on the relationship between the two classes. + * + * Typically, each \c SmartLineEdit and \c SmartLabel instance are declared in a dialog's Qt Designer UI File + * (\c ui/hopEditor.ui). After it is constructed, the needs to be configured via \c SmartLineEdit::configure. + * + * This two-step set-up is needed because AFAIK there is no way to pass constructor parameters to an object in a + * .ui file. (If you want to do that, the advice seems to be to build the layout manually in C++ code.) + * + * Similarly, we might think to template \c SmartLineEdit, but the Qt Meta-Object Compiler (moc) doesn't + * understand C++ templates. This means we can't template classes that need to use the \c Q_OBJECT macro + * (required for classes that declare their own signals and slots or that use other services provided by Qt's + * meta-object system). + * + * Previously we had a class called \c BtLineEdit (on which this class is based) and used mostly trivial + * subclassing to determine the class behaviour. This was a sort of trick to pass in a constructor parameter. + * Classes \c BtStringEdit, \c BtPercentageEdit, would all inherit from \c BtLnneEdit and all do no more than + * pass different parameters to the \c BtLineEdit constructor. This sort of worked when we had relatively few + * \c Measurement::UnitSystem classes but was starting to get unwieldy when those were expanded in anticipation + * of lots of new field types for BeerJSON. Also, it had the disadvantage that you had to think about field + * types when editing the .ui file rather than being able to leave such details to the corresponding .cpp file. + */ +class SmartLineEdit : public QLineEdit { + Q_OBJECT + +public: + + SmartLineEdit(QWidget* parent = nullptr); + virtual ~SmartLineEdit(); + + /** + * \brief TODO write this! + */ + void configure(BtFieldType const fieldType = NonPhysicalQuantity::String, + int const defaultPrecision = 3, + QString const & maximalDisplayString = "100.000 srm"); + +private: + // Private implementation details - see https://herbsutter.com/gotw/_100/ + class impl; + std::unique_ptr pimpl; +}; + +#endif diff --git a/src/xml/XmlRecord.cpp b/src/xml/XmlRecord.cpp index 421735f5..1556976d 100644 --- a/src/xml/XmlRecord.cpp +++ b/src/xml/XmlRecord.cpp @@ -211,10 +211,14 @@ bool XmlRecord::load(xalanc::DOMSupport & domSupport, // I think it gets messy to have different types there than on the QProperty setters. It's not much // overhead to do things here IMHO.) // - // NB: propertyName is not actually a property name when fieldType is RequiredConstant + // Note that: + // - propertyName is not actually a property name when fieldType is RequiredConstant + // - when propertyName is not set, there is nothing to look up (because this is a field we don't + // support, usually an "Extension tag") // bool const propertyIsOptional { - (fieldDefinition->fieldType == XmlRecord::FieldType::RequiredConstant) ? + (fieldDefinition->fieldType == XmlRecord::FieldType::RequiredConstant || + fieldDefinition->propertyName.isNull()) ? false : this->typeLookup->isOptional(fieldDefinition->propertyName) }; @@ -856,10 +860,14 @@ void XmlRecord::toXml(NamedEntity const & namedEntityToExport, // skip writing it out. Strong typing of std::optional makes this a bit more work here (but it helps us in // other ways elsewhere). // - // NB: propertyName is not actually a property name when fieldType is RequiredConstant + // Note that: + // - propertyName is not actually a property name when fieldType is RequiredConstant + // - when propertyName is not set, there is nothing to look up (because this is a field we don't support, + // usually an "Extension tag") // bool const propertyIsOptional { - (fieldDefinition.fieldType == XmlRecord::FieldType::RequiredConstant) ? + (fieldDefinition.fieldType == XmlRecord::FieldType::RequiredConstant || + fieldDefinition.propertyName.isNull()) ? false : this->typeLookup->isOptional(fieldDefinition.propertyName) }; switch (fieldDefinition.fieldType) { From 2181ad6ed17dae1192d004fdf5a4d2b13a57c114 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sun, 26 Mar 2023 10:44:05 +0200 Subject: [PATCH 02/42] Refactoring --- meson.build | 3 + src/BtAmountEdit.cpp | 98 +++++---- src/BtAmountEdit.h | 19 +- src/BtLabel.cpp | 2 - src/CMakeLists.txt | 1 + src/OgAdjuster.cpp | 3 +- src/RefractoDialog.cpp | 8 +- src/UiAmountWithUnits.cpp | 298 +++++++++++++++++++--------- src/UiAmountWithUnits.h | 123 +++++++----- src/utils/TypeLookup.h | 2 +- src/widgets/BtAmountDigitWidget.cpp | 25 ++- src/widgets/BtAmountDigitWidget.h | 21 +- src/widgets/SmartLabel.cpp | 266 +++++++++++++++++++++++++ src/widgets/SmartLabel.h | 148 ++++++++++++++ src/widgets/SmartLineEdit.cpp | 158 ++++++++++++++- src/widgets/SmartLineEdit.h | 73 ++++++- translations/bt_ca.ts | 7 + translations/bt_cs.ts | 7 + translations/bt_de.ts | 7 + translations/bt_el.ts | 7 + translations/bt_en.ts | 7 + translations/bt_es.ts | 7 + translations/bt_et.ts | 7 + translations/bt_eu.ts | 7 + translations/bt_fr.ts | 7 + translations/bt_gl.ts | 7 + translations/bt_hu.ts | 7 + translations/bt_it.ts | 7 + translations/bt_lv.ts | 7 + translations/bt_nb.ts | 7 + translations/bt_nl.ts | 7 + translations/bt_pl.ts | 7 + translations/bt_pt.ts | 7 + translations/bt_ru.ts | 7 + translations/bt_sr.ts | 7 + translations/bt_sv.ts | 7 + translations/bt_tr.ts | 7 + translations/bt_zh.ts | 7 + 38 files changed, 1145 insertions(+), 257 deletions(-) create mode 100644 src/widgets/SmartLabel.cpp create mode 100644 src/widgets/SmartLabel.h diff --git a/meson.build b/meson.build index 6a44b3ff..d56d8e6c 100644 --- a/meson.build +++ b/meson.build @@ -730,6 +730,8 @@ commonSourceFiles = files([ 'src/widgets/BtAmountDigitWidget.cpp', 'src/widgets/BtDigitWidget.cpp', 'src/widgets/SelectionControl.cpp', + 'src/widgets/SmartLabel.cpp', + 'src/widgets/SmartLineEdit.cpp', 'src/widgets/ToggleSwitch.cpp', 'src/widgets/UnitAndScalePopUpMenu.cpp', 'src/xml/BeerXml.cpp', @@ -864,6 +866,7 @@ mocHeaders = files([ 'src/widgets/BtAmountDigitWidget.h', 'src/widgets/BtDigitWidget.h', 'src/widgets/SelectionControl.h', + 'src/widgets/SmartLabel.h', 'src/widgets/ToggleSwitch.h', 'src/YeastDialog.h', 'src/YeastEditor.h', diff --git a/src/BtAmountEdit.cpp b/src/BtAmountEdit.cpp index 8ec78124..a033bec8 100644 --- a/src/BtAmountEdit.cpp +++ b/src/BtAmountEdit.cpp @@ -31,15 +31,14 @@ BtAmountEdit::BtAmountEdit(QWidget *parent, Measurement::PhysicalQuantities const physicalQuantities, - Measurement::Unit const * units, int const defaultPrecision, QString const & maximalDisplayString) : BtLineEdit{parent, ConvertToBtFieldType(physicalQuantities), defaultPrecision, maximalDisplayString}, - UiAmountWithUnits{parent, physicalQuantities, units} { - this->configSection = property("configSection").toString(); + UiAmountWithUnits{parent, physicalQuantities} { + this->setConfigSection(this->property("configSection").toString()); connect(this, &QLineEdit::editingFinished, this, &BtAmountEdit::onLineChanged); @@ -48,13 +47,17 @@ BtAmountEdit::BtAmountEdit(QWidget *parent, BtAmountEdit::~BtAmountEdit() = default; -QString BtAmountEdit::getWidgetText() const { - return this->text(); -} - -void BtAmountEdit::setWidgetText(QString text) { - this->QLineEdit::setText(text); - return; +///QString BtAmountEdit::getWidgetText() const { +/// return this->text(); +///} +/// +///void BtAmountEdit::setWidgetText(QString text) { +/// this->QLineEdit::setText(text); +/// return; +///} + +Measurement::Amount BtAmountEdit::toCanonical() const { + return this->rawToCanonical(this->text()); } void BtAmountEdit::setText(double amount) { @@ -74,10 +77,10 @@ void BtAmountEdit::setText(NamedEntity * element) { } void BtAmountEdit::setText(NamedEntity * element, int precision) { - char const * const propertyName = this->editField.toLatin1().constData(); - QVariant const propertyValue = element->property(propertyName); + QString const propertyName = this->getEditField(); + QVariant const propertyValue = element->property(propertyName.toLatin1().constData()); qDebug() << - Q_FUNC_INFO << "Read property" << this->editField << "(" << propertyName << ") of" << *element << "as" << + Q_FUNC_INFO << "Read property" << propertyName << "(" << propertyName << ") of" << *element << "as" << propertyValue; bool force = false; @@ -89,8 +92,8 @@ void BtAmountEdit::setText(NamedEntity * element, int precision) { double amount = propertyValue.toDouble(&ok); if (!ok) { qWarning() << - Q_FUNC_INFO << "Could not convert " << propertyValue.toString() << " (" << this->configSection << ":" << - this->editField << ") to double"; + Q_FUNC_INFO << "Could not convert " << propertyValue.toString() << " (" << this->getConfigSection() << ":" << + propertyName << ") to double"; } display = this->displayAmount(amount, precision); @@ -113,7 +116,7 @@ void BtAmountEdit::setText(QString amount, int precision) { double amt = Localization::toDouble(amount, &ok); if (!ok) { qWarning() << - Q_FUNC_INFO << "Could not convert" << amount << "(" << this->configSection << ":" << this->editField << + Q_FUNC_INFO << "Could not convert" << amount << "(" << this->getConfigSection() << ":" << this->getEditField() << ") to double"; } this->QLineEdit::setText(this->displayAmount(amt, precision)); @@ -135,17 +138,17 @@ void BtAmountEdit::setText(QVariant amount, int precision) { void BtAmountEdit::onLineChanged() { auto const myFieldType = this->getFieldType(); qDebug() << - Q_FUNC_INFO << "this->fieldType=" << myFieldType << ", this->units=" << this->units << + Q_FUNC_INFO << "this->fieldType=" << myFieldType << ", this->m_canonicalUnits=" << this->m_canonicalUnits << ", forcedSystemOfMeasurement=" << this->getForcedSystemOfMeasurement() << ", forcedRelativeScale=" << this->getForcedRelativeScale() << ", value=" << this->text(); Measurement::PhysicalQuantities const physicalQuantities = ConvertToPhysicalQuantities(myFieldType); + QString const propertyName = this->getEditField(); + QString const configSection = this->getConfigSection(); Measurement::SystemOfMeasurement const oldSystemOfMeasurement = - Measurement::getSystemOfMeasurementForField(this->editField, - this->configSection, - physicalQuantities); - auto oldForcedRelativeScale = Measurement::getForcedRelativeScaleForField(this->editField, this->configSection); + Measurement::getSystemOfMeasurementForField(propertyName, configSection, physicalQuantities); + auto oldForcedRelativeScale = Measurement::getForcedRelativeScaleForField(propertyName, configSection); PreviousScaleInfo previousScaleInfo{ oldSystemOfMeasurement, oldForcedRelativeScale @@ -168,7 +171,7 @@ void BtAmountEdit::lineChanged(PreviousScaleInfo previousScaleInfo) { return; } - this->textOrUnitsChanged(previousScaleInfo); + this->QLineEdit::setText(this->correctEnteredText(this->text(), previousScaleInfo)); if (sender() == this) { emit textModified(); @@ -177,40 +180,33 @@ void BtAmountEdit::lineChanged(PreviousScaleInfo previousScaleInfo) { return; } -// TBD Can we not work out the canonical units here automatically? -BtMassEdit ::BtMassEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Mass , &Measurement::Units::kilograms ) { return; } -BtVolumeEdit ::BtVolumeEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Volume , &Measurement::Units::liters ) { return; } -BtTimeEdit ::BtTimeEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Time , &Measurement::Units::minutes , 3) { return; } -BtTemperatureEdit ::BtTemperatureEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Temperature , &Measurement::Units::celsius , 1) { return; } -BtColorEdit ::BtColorEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Color , &Measurement::Units::srm ) { return; } -BtDensityEdit ::BtDensityEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Density , &Measurement::Units::sp_grav ) { return; } -BtDiastaticPowerEdit ::BtDiastaticPowerEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::DiastaticPower , &Measurement::Units::lintner ) { return; } -BtAcidityEdit ::BtAcidityEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Acidity , &Measurement::Units::pH ) { return; } -BtBitternessEdit ::BtBitternessEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Bitterness , &Measurement::Units::ibu ) { return; } -BtCarbonationEdit ::BtCarbonationEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Carbonation , &Measurement::Units::carbonationVolumes ) { return; } -BtMassConcentrationEdit ::BtMassConcentrationEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::MassConcentration , &Measurement::Units::partsPerMillion ) { return; } -BtVolumeConcentrationEdit ::BtVolumeConcentrationEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::VolumeConcentration , &Measurement::Units::milligramsPerLiter ) { return; } -BtViscosityEdit ::BtViscosityEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Viscosity , &Measurement::Units::centipoise ) { return; } -BtSpecificHeatCapacityEdit::BtSpecificHeatCapacityEdit(QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::SpecificHeatCapacity, &Measurement::Units::caloriesPerCelsiusPerGram) { return; } - - -BtMixedMassOrVolumeEdit::BtMixedMassOrVolumeEdit(QWidget *parent) : - BtAmountEdit(parent, - Measurement::PqEitherMassOrVolume, - // This is probably pure evil I will later regret - &Measurement::Units::liters) { - return; -} +BtMassEdit ::BtMassEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Mass ) { return; } +BtVolumeEdit ::BtVolumeEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Volume ) { return; } +BtTimeEdit ::BtTimeEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Time , 3) { return; } +BtTemperatureEdit ::BtTemperatureEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Temperature , 1) { return; } +BtColorEdit ::BtColorEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Color ) { return; } +BtDensityEdit ::BtDensityEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Density ) { return; } +BtDiastaticPowerEdit ::BtDiastaticPowerEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::DiastaticPower ) { return; } +BtAcidityEdit ::BtAcidityEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Acidity ) { return; } +BtBitternessEdit ::BtBitternessEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Bitterness ) { return; } +BtCarbonationEdit ::BtCarbonationEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Carbonation ) { return; } +BtMassConcentrationEdit ::BtMassConcentrationEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::MassConcentration ) { return; } +BtVolumeConcentrationEdit ::BtVolumeConcentrationEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::VolumeConcentration ) { return; } +BtViscosityEdit ::BtViscosityEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Viscosity ) { return; } +BtSpecificHeatCapacityEdit::BtSpecificHeatCapacityEdit(QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::SpecificHeatCapacity ) { return; } +BtMixedMassOrVolumeEdit ::BtMixedMassOrVolumeEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PqEitherMassOrVolume ) { return; } void BtMixedMassOrVolumeEdit::setIsWeight(bool state) { + qDebug() << Q_FUNC_INFO << "state is" << state; // But you have to admit, this is clever - if (state) { - this->units = &Measurement::Units::kilograms; - } else { - this->units = &Measurement::Units::liters; - } + this->selectPhysicalQuantity(state ? Measurement::PhysicalQuantity::Mass : Measurement::PhysicalQuantity::Volume); // maybe? My head hurts now this->onLineChanged(); + + // Strictly, if we change a Misc to be measured by mass instead of volume (or vice versa) we should also somehow tell + // any other bit of the UI that is showing that Misc (eg a RecipeEditor or MainWindow) to redisplay the relevant + // field. Currently we don't do this, on the assumption that it's rare you will change how a Misc is measured after + // you started using it in recipes. return; } diff --git a/src/BtAmountEdit.h b/src/BtAmountEdit.h index 1f65b79e..50c471c1 100644 --- a/src/BtAmountEdit.h +++ b/src/BtAmountEdit.h @@ -49,20 +49,21 @@ class BtAmountEdit : public BtLineEdit, public UiAmountWithUnits { public: BtAmountEdit(QWidget * parent, Measurement::PhysicalQuantities const physicalQuantities, - Measurement::Unit const * units, int const defaultPrecision = 3, QString const & maximalDisplayString = "100.000 srm"); virtual ~BtAmountEdit(); - /** - * \see \c UiAmountWithUnits for what this member function needs to do - */ - virtual QString getWidgetText() const; +/// /** +/// * \see \c UiAmountWithUnits for what this member function needs to do +/// */ +/// virtual QString getWidgetText() const; +/// +/// /** +/// * \see \c UiAmountWithUnits for what this member function needs to do +/// */ +/// virtual void setWidgetText(QString text); - /** - * \see \c UiAmountWithUnits for what this member function needs to do - */ - virtual void setWidgetText(QString text); + Measurement::Amount toCanonical() const; // Use one of these when you just want to set the text void setText(NamedEntity* element); diff --git a/src/BtLabel.cpp b/src/BtLabel.cpp index 4bd5e37e..a68e8bae 100644 --- a/src/BtLabel.cpp +++ b/src/BtLabel.cpp @@ -122,8 +122,6 @@ void BtLabel::initializeProperty() { return; } - - void BtLabel::initializeMenu() { // If a context menu already exists, we need to delete it and recreate it. We can't always reuse an existing menu // because the sub-menu for relative scale needs to change when a different unit system is selected. (In theory we diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4fbeaea7..5bf12476 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -190,6 +190,7 @@ set(filesToCompile_cpp ${repoDir}/src/widgets/BtAmountDigitWidget.cpp ${repoDir}/src/widgets/BtDigitWidget.cpp ${repoDir}/src/widgets/SelectionControl.cpp + ${repoDir}/src/widgets/SmartLabel.cpp ${repoDir}/src/widgets/SmartLineEdit.cpp ${repoDir}/src/widgets/ToggleSwitch.cpp ${repoDir}/src/widgets/UnitAndScalePopUpMenu.cpp diff --git a/src/OgAdjuster.cpp b/src/OgAdjuster.cpp index 67abee5c..0b187df4 100644 --- a/src/OgAdjuster.cpp +++ b/src/OgAdjuster.cpp @@ -21,6 +21,7 @@ #include "OgAdjuster.h" #include "Algorithms.h" +#include "measurement/Measurement.h" #include "measurement/Unit.h" #include "model/Equipment.h" #include "model/Recipe.h" @@ -48,7 +49,7 @@ void OgAdjuster::calculate() { // Get inputs. double sg = lineEdit_sg->toCanonical().quantity(); bool okPlato = true; - double plato = lineEdit_plato->toDoubleRaw(&okPlato); + double plato = Measurement::extractRawFromString(lineEdit_plato->text(), &okPlato); double temp_c = lineEdit_temp->toCanonical().quantity(); double hydroTemp_c = lineEdit_calTemp->toCanonical().quantity(); double wort_l = lineEdit_volume->toCanonical().quantity(); diff --git a/src/RefractoDialog.cpp b/src/RefractoDialog.cpp index 9b435673..8892c0a9 100644 --- a/src/RefractoDialog.cpp +++ b/src/RefractoDialog.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * RefractoDialog.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * RefractoDialog.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Eric Tamme * • Matt Young @@ -45,9 +45,9 @@ void RefractoDialog::calculate() { // User can enter in specific gravity or Plato, but the lineEdit is going to convert it to Plato, so we can just // grab the number - double originalPlato = lineEdit_op->toDoubleRaw(&haveOP); - double inputOG = lineEdit_inputOG->toDoubleRaw(&haveOG); - double currentPlato = lineEdit_cp->toDoubleRaw(&haveCP); + double originalPlato = Measurement::extractRawFromString(lineEdit_op ->text(), &haveOP); + double inputOG = Measurement::extractRawFromString(lineEdit_inputOG->text(), &haveOG); + double currentPlato = Measurement::extractRawFromString(lineEdit_cp ->text(), &haveCP); clearOutputFields(); diff --git a/src/UiAmountWithUnits.cpp b/src/UiAmountWithUnits.cpp index 6e7287b6..0d2a6c68 100644 --- a/src/UiAmountWithUnits.cpp +++ b/src/UiAmountWithUnits.cpp @@ -32,45 +32,122 @@ #include "measurement/Measurement.h" #include "utils/OptionalHelpers.h" -UiAmountWithUnits::UiAmountWithUnits(QWidget * parent, - Measurement::PhysicalQuantities const physicalQuantities, - Measurement::Unit const * units) : - parent{parent}, - physicalQuantities{physicalQuantities}, - units{units} { +// This private implementation class holds all private non-virtual members of UiAmountWithUnits +class UiAmountWithUnits::impl { +public: + impl(UiAmountWithUnits & self, + QWidget const * const parent, + Measurement::PhysicalQuantities const physicalQuantities) : + m_self {self}, + m_parent {parent}, + m_allowedPhysicalQuantities{physicalQuantities}, + m_currentPhysicalQuantity { + // If the field supports more than one PhysicalQuantity (eg PqEitherMassOrVolume or + // PqEitherMassOrVolumeConcentration), our starting assumption is that we hold the second one (eg Volume or + // VolumeConcentration). Currently this matters because the assumption is baked into the UI of MiscEditor, + // but we should change that at some point. + std::holds_alternative(physicalQuantities) ? + std::get(physicalQuantities) : + std::get<1>(std::get(physicalQuantities)) + } { + return; + } + + ~impl() = default; + + /** + * \brief Returns the contents of the field converted, if necessary, to SI units + * + * \param enteredText + * \param previousScaleInfo + */ + Measurement::Amount toCanonical(QString const & enteredText, PreviousScaleInfo previousScaleInfo) { + qDebug() << + Q_FUNC_INFO << "enteredText:" << enteredText << ", old SystemOfMeasurement:" << + previousScaleInfo.oldSystemOfMeasurement << ", old ForcedScale: " << previousScaleInfo.oldForcedScale; + + Measurement::UnitSystem const & oldUnitSystem = + Measurement::UnitSystem::getInstance(previousScaleInfo.oldSystemOfMeasurement, + this->m_currentPhysicalQuantity); + + Measurement::Unit const * defaultUnit{ + previousScaleInfo.oldForcedScale ? oldUnitSystem.scaleUnit(*previousScaleInfo.oldForcedScale) : oldUnitSystem.unit() + }; + + // It's a coding error if defaultUnit is null, because it means previousScaleInfo.oldForcedScale was not valid for + // oldUnitSystem. However, we can recover. + if (!defaultUnit) { + qWarning() << Q_FUNC_INFO << "previousScaleInfo.oldForcedScale invalid?" << previousScaleInfo.oldForcedScale; + defaultUnit = oldUnitSystem.unit(); + } + + // + // Normally, we display units with the text. If the user just edits the number, then the units will still be there. + // Alternatively, if the user specifies different units in the text, we should try to honour those. Otherwise, if, + // no units are specified in the text, we need to go to defaults. Defaults are either what is "forced" for this + // specific field or, failing that, what is configured globally. + // + // Measurement::UnitSystem::qStringToSI will handle all the logic to deal with any units specified by the user in the + // string. (In theory, we just grab the units that the user has specified in the input text. In reality, it's not + // that easy as we sometimes need to disambiguate - eg between Imperial gallons and US customary ones. So, if we + // have old or current units then that helps with this - eg, if current units are US customary cups and user enters + // gallons, then we'll go with US customary gallons over Imperial ones.) + // + auto amount = oldUnitSystem.qstringToSI(enteredText, *defaultUnit); + qDebug() << Q_FUNC_INFO << "Converted to" << amount; + return amount; + } + + UiAmountWithUnits & m_self; + QWidget const * const m_parent; + Measurement::PhysicalQuantities const m_allowedPhysicalQuantities; + Measurement::PhysicalQuantity m_currentPhysicalQuantity; + QString m_editField; + QString m_configSection; + +}; + + +UiAmountWithUnits::UiAmountWithUnits(QWidget const * const parent, + Measurement::PhysicalQuantities const physicalQuantities) : + pimpl{std::make_unique(*this, parent, physicalQuantities)} { return; } UiAmountWithUnits::~UiAmountWithUnits() = default; Measurement::PhysicalQuantity UiAmountWithUnits::getPhysicalQuantity() const { - // If it's not a "mixed" field (ie one in which the measurement could be for more than one - // Measurement::PhysicalQuantity, eg Mass & Volume), just return what it is - if (std::holds_alternative(this->physicalQuantities)) { - return std::get(this->physicalQuantities); - } - // If it is a "mixed" field, we should return the physical quantity corresponding to the current units - auto const physicalQuantity = this->units->getPhysicalQuantity(); - // It's a coding error if we somehow have units other than Mass or Volume - auto const & tupleOfPqs = std::get(this->physicalQuantities); + return this->pimpl->m_currentPhysicalQuantity; +} + +void UiAmountWithUnits::selectPhysicalQuantity(Measurement::PhysicalQuantity const physicalQuantity) { + // It's a coding error to call this if we only hold one PhysicalQuantity + Q_ASSERT(!std::holds_alternative(this->pimpl->m_allowedPhysicalQuantities)); + + // It's a coding error to try to select a PhysicalQuantity that was not specified in the constructor + auto const & tupleOfPqs = std::get(this->pimpl->m_allowedPhysicalQuantities); Q_ASSERT(std::get<0>(tupleOfPqs) == physicalQuantity || std::get<1>(tupleOfPqs) == physicalQuantity); - return physicalQuantity; + + this->pimpl->m_currentPhysicalQuantity = physicalQuantity; + return; } void UiAmountWithUnits::setForcedSystemOfMeasurement(std::optional systemOfMeasurement) { - Measurement::setForcedSystemOfMeasurementForField(this->editField, this->configSection, systemOfMeasurement); + Measurement::setForcedSystemOfMeasurementForField(this->pimpl->m_editField, + this->pimpl->m_configSection, + systemOfMeasurement); return; } std::optional UiAmountWithUnits::getForcedSystemOfMeasurement() const { - return Measurement::getForcedSystemOfMeasurementForField(this->editField, this->configSection); + return Measurement::getForcedSystemOfMeasurementForField(this->pimpl->m_editField, this->pimpl->m_configSection); } void UiAmountWithUnits::setForcedSystemOfMeasurementViaString(QString systemOfMeasurementAsString) { qDebug() << - Q_FUNC_INFO << "Measurement system" << systemOfMeasurementAsString << "for" << this->configSection << ">" << - this->editField; + Q_FUNC_INFO << "Measurement system" << systemOfMeasurementAsString << "for" << this->pimpl->m_configSection << + ">" << this->pimpl->m_editField; this->setForcedSystemOfMeasurement(Measurement::getFromUniqueName(systemOfMeasurementAsString)); return; } @@ -81,17 +158,18 @@ QString UiAmountWithUnits::getForcedSystemOfMeasurementViaString() const { } void UiAmountWithUnits::setForcedRelativeScale(std::optional relativeScale) { - Measurement::setForcedRelativeScaleForField(this->editField, this->configSection, relativeScale); + Measurement::setForcedRelativeScaleForField(this->pimpl->m_editField, this->pimpl->m_configSection, relativeScale); return; } std::optional UiAmountWithUnits::getForcedRelativeScale() const { - return Measurement::getForcedRelativeScaleForField(this->editField, this->configSection); + return Measurement::getForcedRelativeScaleForField(this->pimpl->m_editField, this->pimpl->m_configSection); } void UiAmountWithUnits::setForcedRelativeScaleViaString(QString relativeScaleAsString) { qDebug() << - Q_FUNC_INFO << "Scale" << relativeScaleAsString << "for" << this->configSection << ">" << this->editField; + Q_FUNC_INFO << "Scale" << relativeScaleAsString << "for" << this->pimpl->m_configSection << ">" << + this->pimpl->m_editField; this->setForcedRelativeScale(Measurement::UnitSystem::getScaleFromUniqueName(relativeScaleAsString)); return; } @@ -102,80 +180,75 @@ QString UiAmountWithUnits::getForcedRelativeScaleViaString() const { } void UiAmountWithUnits::setEditField(QString editField) { - this->editField = editField; + this->pimpl->m_editField = editField; return; } QString UiAmountWithUnits::getEditField() const { - return this->editField; + return this->pimpl->m_editField; } void UiAmountWithUnits::setConfigSection(QString configSection) { // The cascade looks a little odd, but it is intentional. - this->configSection = configSection; - if (this->configSection.isEmpty()) { - this->configSection = this->parent->property("configSection").toString(); + this->pimpl->m_configSection = configSection; + if (this->pimpl->m_configSection.isEmpty()) { + this->pimpl->m_configSection = this->pimpl->m_parent->property("configSection").toString(); } - if (this->configSection.isEmpty()) { - this->configSection = this->parent->objectName(); + if (this->pimpl->m_configSection.isEmpty()) { + this->pimpl->m_configSection = this->pimpl->m_parent->objectName(); } return; } QString UiAmountWithUnits::getConfigSection() { - if (this->configSection.isEmpty()) { + if (this->pimpl->m_configSection.isEmpty()) { // Setting the config section to blank will actually attempt to populate it with default values -- see // UiAmountWithUnits::setConfigSection() this->setConfigSection(""); } - return this->configSection; + return this->pimpl->m_configSection; } -double UiAmountWithUnits::toDoubleRaw(bool * ok) const { - return Measurement::extractRawFromString(this->getWidgetText(), ok); -} +///double UiAmountWithUnits::toDoubleRaw(bool * ok) const { +/// return Measurement::extractRawFromString(this->getWidgetText(), ok); +///} -Measurement::Amount UiAmountWithUnits::toCanonical() const { - Q_ASSERT(this->units); - return Measurement::qStringToSI(this->getWidgetText(), - this->units->getPhysicalQuantity(), +Measurement::Amount UiAmountWithUnits::rawToCanonical(QString const & rawValue) const { + return Measurement::qStringToSI(rawValue, + this->pimpl->m_currentPhysicalQuantity, this->getForcedSystemOfMeasurement(), this->getForcedRelativeScale()); } - QString UiAmountWithUnits::displayAmount(double amount, int precision) const { // I find this a nice level of abstraction. This lets all of the setText() // methods make a single call w/o having to do the logic for finding the // unit and scale. - if (this->units) { - return Measurement::displayAmount(Measurement::Amount{amount, *this->units}, - precision, - this->getForcedSystemOfMeasurement(), - this->getForcedRelativeScale()); - } - return Measurement::displayQuantity(amount, precision); + return Measurement::displayAmount( + Measurement::Amount{amount, Measurement::Unit::getCanonicalUnit(this->pimpl->m_currentPhysicalQuantity)}, + precision, + this->getForcedSystemOfMeasurement(), + this->getForcedRelativeScale() + ); } -void UiAmountWithUnits::textOrUnitsChanged(PreviousScaleInfo previousScaleInfo) { - // This is where it gets hard - // - // We may need to fix the text that the user entered, eg if this field is set to show US Customary volumes and user - // enters an amount in litres then we need to convert it to display in pints or quarts etc. +QString UiAmountWithUnits::correctEnteredText(QString const & enteredText, PreviousScaleInfo previousScaleInfo) { QString correctedText; - QString rawValue = this->getWidgetText(); - qDebug() << Q_FUNC_INFO << "rawValue:" << rawValue; + qDebug() << Q_FUNC_INFO << "enteredText:" << enteredText; - if (rawValue.isEmpty()) { - return; + if (enteredText.isEmpty()) { + return enteredText; } // The idea here is we need to first translate the field into a known // amount (aka to SI) and then into the unit we want. - Measurement::Amount amountAsCanonical = this->convertToSI(previousScaleInfo); + Measurement::Amount amountAsCanonical = this->pimpl->toCanonical(enteredText, previousScaleInfo); + // + // .:TBD:. This seems like a hack we should do away with... + // Measurement::PhysicalQuantity physicalQuantity = this->getPhysicalQuantity(); int precision = 3; if (physicalQuantity == Measurement::PhysicalQuantity::Color) { @@ -183,48 +256,75 @@ void UiAmountWithUnits::textOrUnitsChanged(PreviousScaleInfo previousScaleInfo) } correctedText = this->displayAmount(amountAsCanonical.quantity(), precision); qDebug() << - Q_FUNC_INFO << "Interpreted" << rawValue << "as" << amountAsCanonical << "and corrected to" << correctedText; - qDebug() << Q_FUNC_INFO << "this->units=" << this->units; + Q_FUNC_INFO << "Interpreted" << enteredText << "as" << amountAsCanonical << "and corrected to" << correctedText; - this->setWidgetText(correctedText); - return; + return correctedText; } -Measurement::Amount UiAmountWithUnits::convertToSI(PreviousScaleInfo previousScaleInfo) { - QString rawValue = this->getWidgetText(); - qDebug() << - Q_FUNC_INFO << "rawValue:" << rawValue << ", old SystemOfMeasurement:" << previousScaleInfo.oldSystemOfMeasurement << - ", old ForcedScale: " << previousScaleInfo.oldForcedScale; - - Measurement::PhysicalQuantity physicalQuantity = this->getPhysicalQuantity(); - - Measurement::UnitSystem const & oldUnitSystem = - Measurement::UnitSystem::getInstance(previousScaleInfo.oldSystemOfMeasurement, physicalQuantity); - - Measurement::Unit const * defaultUnit{ - previousScaleInfo.oldForcedScale ? oldUnitSystem.scaleUnit(*previousScaleInfo.oldForcedScale) : oldUnitSystem.unit() - }; - - // It's a coding error if defaultUnit is null, because it means previousScaleInfo.oldForcedScale was not valid for - // oldUnitSystem. However, we can recover. - if (!defaultUnit) { - qWarning() << Q_FUNC_INFO << "previousScaleInfo.oldForcedScale invalid?" << previousScaleInfo.oldForcedScale; - defaultUnit = oldUnitSystem.unit(); - } - - // - // Normally, we display units with the text. If the user just edits the number, then the units will still be there. - // Alternatively, if the user specifies different units in the text, we should try to honour those. Otherwise, if, - // no units are specified in the text, we need to go to defaults. Defaults are either what is "forced" for this - // specific field or, failing that, what is configured globally. - // - // Measurement::UnitSystem::qStringToSI will handle all the logic to deal with any units specified by the user in the - // string. (In theory, we just grab the units that the user has specified in the input text. In reality, it's not - // that easy as we sometimes need to disambiguate - eg between Imperial gallons and US customary ones. So, if we - // have old or current units then that helps with this - eg, if current units are US customary cups and user enters - // gallons, then we'll go with US customary gallons over Imperial ones.) - // - auto amount = oldUnitSystem.qstringToSI(rawValue, *defaultUnit); - qDebug() << Q_FUNC_INFO << "Converted to" << amount; - return amount; -} +///void UiAmountWithUnits::textOrUnitsChanged(PreviousScaleInfo previousScaleInfo) { +/// // This is where it gets hard +/// // +/// // We may need to fix the text that the user entered, eg if this field is set to show US Customary volumes and user +/// // enters an amount in litres then we need to convert it to display in pints or quarts etc. +/// QString correctedText; +/// +/// QString rawValue = this->getWidgetText(); +/// qDebug() << Q_FUNC_INFO << "rawValue:" << rawValue; +/// +/// if (rawValue.isEmpty()) { +/// return; +/// } +/// +/// // The idea here is we need to first translate the field into a known +/// // amount (aka to SI) and then into the unit we want. +/// Measurement::Amount amountAsCanonical = this->convertToSI(previousScaleInfo); +/// +/// Measurement::PhysicalQuantity physicalQuantity = this->getPhysicalQuantity(); +/// int precision = 3; +/// if (physicalQuantity == Measurement::PhysicalQuantity::Color) { +/// precision = 0; +/// } +/// correctedText = this->displayAmount(amountAsCanonical.quantity(), precision); +/// qDebug() << +/// Q_FUNC_INFO << "Interpreted" << rawValue << "as" << amountAsCanonical << "and corrected to" << correctedText; +/// +/// this->setWidgetText(correctedText); +/// return; +///} + +///Measurement::Amount UiAmountWithUnits::convertToSI(PreviousScaleInfo previousScaleInfo) { +/// QString rawValue = this->getWidgetText(); +/// qDebug() << +/// Q_FUNC_INFO << "rawValue:" << rawValue << ", old SystemOfMeasurement:" << +/// previousScaleInfo.oldSystemOfMeasurement << ", old ForcedScale: " << previousScaleInfo.oldForcedScale; +/// +/// Measurement::UnitSystem const & oldUnitSystem = +/// Measurement::UnitSystem::getInstance(previousScaleInfo.oldSystemOfMeasurement, this->pimpl->m_currentPhysicalQuantity); +/// +/// Measurement::Unit const * defaultUnit{ +/// previousScaleInfo.oldForcedScale ? oldUnitSystem.scaleUnit(*previousScaleInfo.oldForcedScale) : oldUnitSystem.unit() +/// }; +/// +/// // It's a coding error if defaultUnit is null, because it means previousScaleInfo.oldForcedScale was not valid for +/// // oldUnitSystem. However, we can recover. +/// if (!defaultUnit) { +/// qWarning() << Q_FUNC_INFO << "previousScaleInfo.oldForcedScale invalid?" << previousScaleInfo.oldForcedScale; +/// defaultUnit = oldUnitSystem.unit(); +/// } +/// +/// // +/// // Normally, we display units with the text. If the user just edits the number, then the units will still be there. +/// // Alternatively, if the user specifies different units in the text, we should try to honour those. Otherwise, if, +/// // no units are specified in the text, we need to go to defaults. Defaults are either what is "forced" for this +/// // specific field or, failing that, what is configured globally. +/// // +/// // Measurement::UnitSystem::qStringToSI will handle all the logic to deal with any units specified by the user in the +/// // string. (In theory, we just grab the units that the user has specified in the input text. In reality, it's not +/// // that easy as we sometimes need to disambiguate - eg between Imperial gallons and US customary ones. So, if we +/// // have old or current units then that helps with this - eg, if current units are US customary cups and user enters +/// // gallons, then we'll go with US customary gallons over Imperial ones.) +/// // +/// auto amount = oldUnitSystem.qstringToSI(rawValue, *defaultUnit); +/// qDebug() << Q_FUNC_INFO << "Converted to" << amount; +/// return amount; +///} diff --git a/src/UiAmountWithUnits.h b/src/UiAmountWithUnits.h index c6bcba1e..78224862 100644 --- a/src/UiAmountWithUnits.h +++ b/src/UiAmountWithUnits.h @@ -23,6 +23,7 @@ #define UIAMOUNTWITHUNITS_H #pragma once +#include // For PImpl #include #include @@ -39,36 +40,35 @@ struct PreviousScaleInfo { }; /** - * \class UiAmountWithUnits A base class, suitable for combining with \c QLabel, \c QLineEdit, etc, that handles all the - * unit transformation such a widget would need to do. It is inherited by \c BtDigitWidget and + * \class UiAmountWithUnits A class, suitable for combining with \c QLabel, \c QLineEdit, etc, that handles all the unit + * transformation such a widget would need to do. It is inherited by \c BtDigitWidget and * \c BtAmountEdit. */ class UiAmountWithUnits { public: /** - * \param parent + * \param parent The \c QWidget that "owns" us. Used for looking up config section names for retrieving forced + * scales etc for this individual field * \param physicalQuantities the \c PhysicalQuantity or \c Mixed2PhysicalQuantities to which this amount relates - * \param units */ - UiAmountWithUnits(QWidget * parent, - Measurement::PhysicalQuantities const physicalQuantities, - Measurement::Unit const * units = nullptr); + UiAmountWithUnits(QWidget const * const parent, + Measurement::PhysicalQuantities const physicalQuantities); virtual ~UiAmountWithUnits(); - /** - * \brief A class inheriting from this class is also expected to also inherit from a \c QWidget such as \c QLabel or - * \c QLineEdit. We would like to be able to access the text() member function of that parent class in parts - * of our own implementation. This is a bit tricky as \c QLabel::text() and \c QLineEdit::text() are actually - * unrelated, despite both having the same signature. We therefore require child classes to implement this - * wrapper function that returns the value of \c text() from their other superclass. - */ - virtual QString getWidgetText() const = 0; - - /** - * \brief Similar to \c getText(), this allows this base class to access \c QLabel::setText() or - * \c QLineEdit::setText() in the subclass that also inherits from \c QLabel or \c QLineEdit. - */ - virtual void setWidgetText(QString text) = 0; +/// /** +/// * \brief A class inheriting from this class is also expected to also inherit from a \c QWidget such as \c QLabel or +/// * \c QLineEdit. We would like to be able to access the text() member function of that parent class in parts +/// * of our own implementation. This is a bit tricky as \c QLabel::text() and \c QLineEdit::text() are actually +/// * unrelated, despite both having the same signature. We therefore require child classes to implement this +/// * wrapper function that returns the value of \c text() from their other superclass. +/// */ +/// virtual QString getWidgetText() const = 0; +/// +/// /** +/// * \brief Similar to \c getText(), this allows this base class to access \c QLabel::setText() or +/// * \c QLineEdit::setText() in the subclass that also inherits from \c QLabel or \c QLineEdit. +/// */ +/// virtual void setWidgetText(QString text) = 0; /** * \brief Returns what type of field this is - except that, if it is \c Mixed2PhysicalQuantities, will one of the two @@ -76,6 +76,14 @@ class UiAmountWithUnits { */ Measurement::PhysicalQuantity getPhysicalQuantity() const; + /** + * \brief If the \c Measurement::PhysicalQuantities supplied in the constructor was not a single + * \c Measurement::PhysicalQuantity, then this member function permits selecting the current + * \c Measurement::PhysicalQuantity from two in the \c Measurement::Mixed2PhysicalQuantities supplied in the + * constructor. + */ + void selectPhysicalQuantity(Measurement::PhysicalQuantity const physicalQuantity); + void setForcedSystemOfMeasurement(std::optional systemOfMeasurement); void setForcedRelativeScale(std::optional relativeScale); @@ -113,58 +121,67 @@ class UiAmountWithUnits { void setConfigSection(QString configSection); QString getConfigSection(); - /** - * \brief Converts the numeric part of the input field to a double, ignoring any string suffix. So "5.5 gal" will - * give 5.5, "20L" will return 20.0, and so on. - */ - double toDoubleRaw(bool * ok = nullptr) const; +/// /** +/// * \brief Converts the numeric part of the input field to a double, ignoring any string suffix. So "5.5 gal" will +/// * give 5.5, "20L" will return 20.0, and so on. +/// */ +/// double toDoubleRaw(bool * ok = nullptr) const; /** * \brief Returns the field converted to canonical units for the relevant \c Measurement::PhysicalQuantity + * + * \param rawValue field text to process + * \return */ - Measurement::Amount toCanonical() const; + Measurement::Amount rawToCanonical(QString const & rawValue) const; /** * \brief Use this when you want to do something with the returned QString + * + * \param amount Must be in canonical units eg kilograms for mass, liters for volume */ - QString displayAmount(double amount, int precision = 3) const; - -protected: - /** - * \brief - */ - void textOrUnitsChanged(PreviousScaleInfo previousScaleInfo); + [[nodiscard]] QString displayAmount(double amount, int precision = 3) const; /** - * \brief Returns the contents of the field converted, if necessary, to SI units + * \brief When the user has finished entering some text, this function does the corrections, eg if the field is set + * to show US Customary volumes and user enters an amount in liters (aka litres) then we need to convert it to + * display in pints or quarts etc. + * \param enteredText Typically retrieved by caller from \c QLabel::text() or \c QLineEdit::text() + * \param previousScaleInfo * - * \param oldSystemOfMeasurement - * \param oldScale (optional) + * \return Corrected text that caller should typically pass back to \c QLabel::setText() or \c QLineEdit::setText() */ - Measurement::Amount convertToSI(PreviousScaleInfo previousScaleInfo); + [[nodiscard]] QString correctEnteredText(QString const & enteredText, PreviousScaleInfo previousScaleInfo); +protected: +/// /** +/// * \brief +/// */ +/// void textOrUnitsChanged(PreviousScaleInfo previousScaleInfo); private: - QWidget * parent; - /** - * \brief Even inside the class (or any subclasses), this should never be accessed directly but always through - * \c this->getPhysicalQuantity, as there is special case handling for \c Mixed2PhysicalQuantities. - */ - Measurement::PhysicalQuantities const physicalQuantities; +/// /** +/// * \brief Returns the contents of the field converted, if necessary, to SI units +/// * +/// * \param oldSystemOfMeasurement +/// * \param oldScale (optional) +/// */ +/// Measurement::Amount convertToSI(PreviousScaleInfo previousScaleInfo); protected: /** - * \brief If \c fieldType is a \c Measurement::PhysicalQuantity, this is the \c Measurement::Unit that should be used - * to store the amount of this field. This is normally fixed as our "standard" (normally metric) unit for the - * \c Measurement::PhysicalQuantity of the field -- eg kilograms for Mass, liters for Volume, - * celsius for Temperature, minutes for Time, etc. However, for \c fieldType of + * \brief If \c physicalQuantities is a \c Measurement::PhysicalQuantity, this is the \c Measurement::Unit that + * should be used to store the amount of this field. This is normally fixed as our "standard" (normally + * metric) unit for the \c Measurement::PhysicalQuantity of the field -- eg kilograms for Mass, liters for + * Volume, celsius for Temperature, minutes for Time, etc. However, for \c physicalQuantities of * \c Mixed2PhysicalQuantities, this will need to vary between two different \c Measurement::Units values * depending on which \c Measurement::PhysicalQuantity the field is currently set to measure. - * - * If \c fieldType is \c NonPhysicalQuantity, this will be \c nullptr */ - Measurement::Unit const * units; - QString editField; - QString configSection; + Measurement::Unit const * m_canonicalUnits; + +private: + // Private implementation details - see https://herbsutter.com/gotw/_100/ + class impl; + std::unique_ptr pimpl; }; #endif diff --git a/src/utils/TypeLookup.h b/src/utils/TypeLookup.h index e779172c..da9c7499 100644 --- a/src/utils/TypeLookup.h +++ b/src/utils/TypeLookup.h @@ -139,7 +139,7 @@ class TypeLookup { using LookupMap = std::map; /** - * \brief Construct a \c TypeLookup that optinoally extends an existing one (typically from the parent class) + * \brief Construct a \c TypeLookup that optionally extends an existing one (typically from the parent class) * * \param className Name of the class for which this is the property type lookup. Eg for the \c TypeLookup for * \c Hop, this should be "Hop". Used for error logging. diff --git a/src/widgets/BtAmountDigitWidget.cpp b/src/widgets/BtAmountDigitWidget.cpp index 141eb21e..9c9f8273 100644 --- a/src/widgets/BtAmountDigitWidget.cpp +++ b/src/widgets/BtAmountDigitWidget.cpp @@ -19,30 +19,29 @@ #include "widgets/BtAmountDigitWidget.h" BtAmountDigitWidget::BtAmountDigitWidget(QWidget * parent, - Measurement::PhysicalQuantities const physicalQuantities, - Measurement::Unit const * units) : + Measurement::PhysicalQuantities const physicalQuantities) : BtDigitWidget{parent, ConvertToBtFieldType(physicalQuantities)}, - UiAmountWithUnits(parent, physicalQuantities, units) { + UiAmountWithUnits(parent, physicalQuantities) { return; } BtAmountDigitWidget::~BtAmountDigitWidget() = default; -QString BtAmountDigitWidget::getWidgetText() const { - return this->text(); -} - -void BtAmountDigitWidget::setWidgetText(QString text) { - this->QLabel::setText(text); - return; -} +///QString BtAmountDigitWidget::getWidgetText() const { +/// return this->text(); +///} +/// +///void BtAmountDigitWidget::setWidgetText(QString text) { +/// this->QLabel::setText(text); +/// return; +///} void BtAmountDigitWidget::displayChanged(PreviousScaleInfo previousScaleInfo) { - this->textOrUnitsChanged(previousScaleInfo); + this->QLabel::setText(this->correctEnteredText(this->text(), previousScaleInfo)); return; } BtMassDigit::BtMassDigit(QWidget* parent) : - BtAmountDigitWidget{parent, Measurement::PhysicalQuantity::Mass, &Measurement::Units::kilograms} { + BtAmountDigitWidget{parent, Measurement::PhysicalQuantity::Mass} { return; } diff --git a/src/widgets/BtAmountDigitWidget.h b/src/widgets/BtAmountDigitWidget.h index b520b128..f1bbcfb6 100644 --- a/src/widgets/BtAmountDigitWidget.h +++ b/src/widgets/BtAmountDigitWidget.h @@ -39,19 +39,18 @@ class BtAmountDigitWidget : public BtDigitWidget, public UiAmountWithUnits { Q_PROPERTY(QString forcedRelativeScale READ getForcedRelativeScaleViaString WRITE setForcedRelativeScaleViaString STORED false) BtAmountDigitWidget(QWidget * parent, - Measurement::PhysicalQuantities const physicalQuantities, - Measurement::Unit const * units = nullptr); + Measurement::PhysicalQuantities const physicalQuantities); virtual ~BtAmountDigitWidget(); - /** - * \see \c UiAmountWithUnits for what this member function needs to do - */ - virtual QString getWidgetText() const; - - /** - * \see \c UiAmountWithUnits for what this member function needs to do - */ - virtual void setWidgetText(QString text); +/// /** +/// * \see \c UiAmountWithUnits for what this member function needs to do +/// */ +/// virtual QString getWidgetText() const; +/// +/// /** +/// * \see \c UiAmountWithUnits for what this member function needs to do +/// */ +/// virtual void setWidgetText(QString text); public slots: /** diff --git a/src/widgets/SmartLabel.cpp b/src/widgets/SmartLabel.cpp new file mode 100644 index 00000000..66f802c4 --- /dev/null +++ b/src/widgets/SmartLabel.cpp @@ -0,0 +1,266 @@ +/*====================================================================================================================== + * widgets/SmartLabel.cpp is part of Brewken, and is copyright the following authors 2009-2023: + * • Brian Rower + * • Mark de Wever + * • Matt Young + * • Mik Firestone + * • Philip Greggory Lee + * + * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see + * . + =====================================================================================================================*/ +#include "widgets/SmartLabel.h" + +#include +#include +#include + +#include "BtAmountEdit.h" +#include "measurement/Measurement.h" +#include "model/Style.h" +#include "model/Recipe.h" +#include "PersistentSettings.h" +#include "utils/OptionalHelpers.h" +#include "widgets/UnitAndScalePopUpMenu.h" + +SmartLabel::SmartLabel(QWidget *parent) : + QLabel{parent}, + btParent{parent}, + contextMenu{nullptr} { + connect(this, &QWidget::customContextMenuRequested, this, &SmartLabel::popContextMenu); + return; +} + +SmartLabel::~SmartLabel() = default; + +SmartLineEdit & SmartLabel::getBuddy() const { + // Call QLabel's built-in function to get the buddy + QWidget * buddy = this->buddy(); + + // We assert that it's a coding error for there not to be a buddy! + Q_ASSERT(buddy); + + return static_cast(*buddy); +} + +void SmartLabel::enterEvent([[maybe_unused]] QEvent * event) { + this->textEffect(true); + return; +} + +void SmartLabel::leaveEvent([[maybe_unused]] QEvent * event) { + this->textEffect(false); + return; +} + +void SmartLabel::mouseReleaseEvent(QMouseEvent * event) { + // For the moment, we want left-click and right-click to have the same effect, so when we get a left-click event, we + // send ourselves the right-click signal, which will then fire SmartLabel::popContextMenu(). + emit this->QWidget::customContextMenuRequested(event->pos()); + return; +} + +void SmartLabel::textEffect(bool enabled) { + QFont myFont = this->font(); + myFont.setUnderline(enabled); + this->setFont(myFont); + return; +} + +void SmartLabel::initializeSection() { + if (!this->configSection.isEmpty()) { + return; + } + + // as much as I dislike it, dynamic properties can't be referenced on + // initialization. + QWidget * mybuddy = this->buddy(); + + // + // If the label has the configSection defined, use it + // otherwise, if the paired field has a configSection, use it + // otherwise, if the parent object has a configSection, use it + // if all else fails, get the parent's object name + // + if (this->property("configSection").isValid()) { + this->configSection = property("configSection").toString(); + } else if (mybuddy && mybuddy->property("configSection").isValid() ) { + this->configSection = mybuddy->property("configSection").toString(); + } else if (this->btParent->property("configSection").isValid() ) { + this->configSection = this->btParent->property("configSection").toString(); + } else { + qWarning() << Q_FUNC_INFO << "this failed" << this; + this->configSection = this->btParent->objectName(); + } + return; +} + +void SmartLabel::initializeProperty() { + + if (!this->propertyName.isEmpty()) { + return; + } + + QWidget* mybuddy = this->buddy(); + if (this->property("editField").isValid()) { + this->propertyName = this->property("editField").toString(); + } else if (mybuddy && mybuddy->property("editField").isValid()) { + this->propertyName = mybuddy->property("editField").toString(); + } else { + qWarning() << Q_FUNC_INFO << "That failed miserably"; + } + return; +} + +void SmartLabel::initializeMenu() { + // If a context menu already exists, we need to delete it and recreate it. We can't always reuse an existing menu + // because the sub-menu for relative scale needs to change when a different unit system is selected. (In theory we + // could only recreate the context menu when a different unit system is selected, but that adds complication.) + if (this->contextMenu) { + // NB: Although the existing menu is "owned" by this->btParent, it is fine for us to delete it here. The Qt + // ownership in this context merely guarantees that this->btParent will, in its own destructor, delete the menu if + // it still exists. + delete this->contextMenu; + this->contextMenu = nullptr; + } + + std::optional forcedSystemOfMeasurement = + Measurement::getForcedSystemOfMeasurementForField(this->propertyName, this->configSection); + std::optional forcedRelativeScale = + Measurement::getForcedRelativeScaleForField(this->propertyName, this->configSection); + qDebug() << + Q_FUNC_INFO << "forcedSystemOfMeasurement=" << forcedSystemOfMeasurement << ", forcedRelativeScale=" << + forcedRelativeScale; + + auto const & buddy = this->getBuddy(); + auto fieldType = buddy.getFieldType(); + if (std::holds_alternative(fieldType)) { + return; + } + + // Since fieldType is not NonPhysicalQuantity this cast should be safe + Measurement::PhysicalQuantity const physicalQuantity = buddy.getUiAmountWithUnits().getPhysicalQuantity(); + this->contextMenu = UnitAndScalePopUpMenu::create(this->btParent, + physicalQuantity, + forcedSystemOfMeasurement, + forcedRelativeScale); + return; +} + +void SmartLabel::popContextMenu(const QPoint& point) { + // For the moment, at least, we do not allow people to choose date formats per-field. (Although you might want to + // mix and match metric and imperial systems in certain circumstances, it's less clear that there's a benefit to + // mixing and matching date formats.) + if (!std::holds_alternative(this->fieldType)) { + return; + } + + QObject* calledBy = sender(); + if (calledBy == nullptr) { + return; + } + + QWidget * widgie = qobject_cast(calledBy); + if (widgie == nullptr) { + return; + } + + this->initializeProperty(); + this->initializeSection(); + this->initializeMenu(); + + // Show the pop-up menu and get back whatever the user seleted + QAction * invoked = this->contextMenu->exec(widgie->mapToGlobal(point)); + if (invoked == nullptr) { + return; + } + + // Save the current settings (which may come from system-wide defaults) for the signal below + Q_ASSERT(std::holds_alternative(this->fieldType)); + Measurement::PhysicalQuantity physicalQuantity = std::get(this->fieldType); + PreviousScaleInfo previousScaleInfo{ + Measurement::getSystemOfMeasurementForField(this->propertyName, this->configSection, physicalQuantity), + Measurement::getForcedRelativeScaleForField(this->propertyName, this->configSection) + }; + + // To make this all work, we need to set ogMin and ogMax when og is set etc + QVector fieldsToSet; + fieldsToSet.append(this->propertyName); + if (this->propertyName == "og") { + fieldsToSet.append(QString(*PropertyNames::Style::ogMin)); + fieldsToSet.append(QString(*PropertyNames::Style::ogMax)); + } else if (this->propertyName == "fg") { + fieldsToSet.append(QString(*PropertyNames::Style::fgMin)); + fieldsToSet.append(QString(*PropertyNames::Style::fgMax)); + } else if (this->propertyName == "color_srm") { + fieldsToSet.append(QString(*PropertyNames::Style::colorMin_srm)); + fieldsToSet.append(QString(*PropertyNames::Style::colorMax_srm)); + } + + // User will either have selected a SystemOfMeasurement or a UnitSystem::RelativeScale. We can know which based on + // whether it's the menu or the sub-menu that it came from. + bool isTopMenu{invoked->parentWidget() == this->contextMenu}; + if (isTopMenu) { + // It's the menu, so SystemOfMeasurement + std::optional whatSelected = + UnitAndScalePopUpMenu::dataFromQAction(*invoked); + qDebug() << Q_FUNC_INFO << "Selected SystemOfMeasurement" << whatSelected; + if (!whatSelected) { + // Null means "Default", which means don't set a forced SystemOfMeasurement for this field + for (auto field : fieldsToSet) { + Measurement::setForcedSystemOfMeasurementForField(field, this->configSection, std::nullopt); + } + } else { + for (auto field : fieldsToSet) { + Measurement::setForcedSystemOfMeasurementForField(field, this->configSection, *whatSelected); + } + } + // Choosing a forced SystemOfMeasurement resets any selection of forced RelativeScale + for (auto field : fieldsToSet) { + Measurement::setForcedRelativeScaleForField(field, this->configSection, std::nullopt); + } + + // + // Hmm. For the color fields, we want to include the ecb or srm in the label text here. + // + // Assert that we already bailed above for fields that aren't a PhysicalQuantity, so we know std::get won't throw + // here. + // + Q_ASSERT(std::holds_alternative(this->fieldType)); + if (Measurement::PhysicalQuantity::Color == std::get(this->fieldType)) { + Measurement::UnitSystem const & disp = + Measurement::getUnitSystemForField(this->propertyName, + this->configSection, + Measurement::PhysicalQuantity::Color); + this->setText(tr("Color (%1)").arg(disp.unit()->name)); + } + } else { + // It's the sub-menu, so UnitSystem::RelativeScale + std::optional whatSelected = + UnitAndScalePopUpMenu::dataFromQAction(*invoked); + qDebug() << Q_FUNC_INFO << "Selected RelativeScale" << whatSelected; + if (!whatSelected) { + // Null means "Default", which means don't set a forced RelativeScale for this field + for (auto field : fieldsToSet) { + Measurement::setForcedRelativeScaleForField(field, this->configSection, std::nullopt); + } + } else { + for (auto field : fieldsToSet) { + Measurement::setForcedRelativeScaleForField(field, this->configSection, *whatSelected); + } + } + } + + // Remember, we need the original unit, not the new one. + emit changedSystemOfMeasurementOrScale(previousScaleInfo); + + return; +} diff --git a/src/widgets/SmartLabel.h b/src/widgets/SmartLabel.h new file mode 100644 index 00000000..4a8a6be2 --- /dev/null +++ b/src/widgets/SmartLabel.h @@ -0,0 +1,148 @@ +/*====================================================================================================================== + * widgets/SmartLabel.h is part of Brewken, and is copyright the following authors 2009-2023: + * • Mark de Wever + * • Matt Young + * • Mik Firestone + * • Philip Greggory Lee + * + * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see + * . + =====================================================================================================================*/ +#ifndef WIDGETS_SMARTLABEL_H +#define WIDGETS_SMARTLABEL_H +#pragma once + +#include + +#include +#include +#include +#include +#include + +#include "BtFieldType.h" +#include "widgets/SmartLineEdit.h" +#include "measurement/UnitSystem.h" +#include "UiAmountWithUnits.h" // For PreviousScaleInfo + +/*! + * \class SmartLabel + * + * \brief Performs the necessary magic to select display units for any label. Specifically, this allows the user to + * right-click on the label for a field and select + * (a) which unit system to use for that field (eg US Customary (mass), Imperial (mass) or Metric/SI (mass) + * for a weight field) + * (b) which units within that system to use for the field (eg kg, g, mg if the user has selected Metric/SI on + * a weight field). + * Moreover, the settings for each label are remembered (via PersistentSettings) for future times the program is + * run. + * + * This has been a rather hidden feature of the program as there were no visual clues that right-clicking on a + * field label would bring up a useful menu (and it is not common behaviour in other software). Where possible, + * we have now made it so that + * • mouseover on the label underlines the label text (hopefully making the user think of a clickable link), + * • where left-clicking would otherwise have no effect, it now has the same effect as right-click. + * + * A \c SmartLabel will usually have a corresponding \c SmartLineEdit. These two widgets will be Qt buddies, + * which mostly just means that the \c SmartLineEdit accepts the input focus on behalf of the \c SmartLabel when + * the user types the label's shortcut key combination. (It also means we don't have to store a bunch of info in + * this object that we can just get from our buddy. Eg \c BtFieldType is stored in \c SmartLineEdit, so we don't + * also need to store it here in \c SmartLabel.) + * + * When the \c SmartLabel needs to tell the \c SmartLineEdit that the \c UnitSystem etc has changed, it sends a + * \c changedUnitSystemOrScale signal. (Previously this signal was called \c labelChanged.) + */ +class SmartLabel : public QLabel { + Q_OBJECT + +public: + /*! + * \brief Initialize the SmartLabel with the parent and do some things with the type + * + * \param parent - QWidget* to the parent object + * + * \todo Not sure if I can get the name of the widget being created. + * Not sure how to signal the parent to redisplay + */ + SmartLabel(QWidget * parent); + virtual ~SmartLabel(); + + /** + * \brief Our "buddy" should always be a \c SmartSmartLineEditLabel. This is a convenience function to get it + * without the caller having to downcast from \c QWidget etc. + */ + SmartLineEdit & getBuddy() const; + + /** + * \brief We override the \c QWidget event handlers \c enterEvent and \c leaveEvent to implement mouse-over effects + * on the label text - specifically to give the user a visual clue that the label text is (right)-clickable + */ + virtual void enterEvent(QEvent* event); + virtual void leaveEvent(QEvent* event); + + /** + * \brief We override the \c QWidget event handler \c mouseReleaseEvent to capture left mouse clicks on us. (Right + * clicks get notified to us via the \c QWidget::customContextMenuRequested signal.) + */ + virtual void mouseReleaseEvent(QMouseEvent * event); + + +private: + void textEffect(bool enabled); + +public slots: + /** + * \brief Shows the pop-up menu to allow the user to override the units and/or scale for this field + */ + void popContextMenu(const QPoint &point); + +signals: + /** + * \brief Signal to say we changed the forced system of measurement and/or scale for a field (or group of fields) + * + * NB: This is mostly referenced in .ui files, which compile to string-based connection syntax (see + * https://doc.qt.io/qt-5/signalsandslots-syntaxes.html for difference from functor-based syntax that we + * generally prefer to use in .cpp files). Note too that, if you are manually editing a .ui file rather than + * using Qt Designer, you must NOT put parameter names in the function declarations in the \ and + * \ tags inside the \ tag. + * + * The idea is that fields affected by a change in forced system of measurement or scale (including to/from + * "default") can take current value, convert it to Metric/SI under the "old" settings, then redisplay it with + * whatever the new settings are. Because the fields don't store the "old" settings, we have to send them. + * (They can get the new ones just by calling \c Measurement::getUnitSystemForField() etc. + * + * There will always be an old \c SystemOfMeasurement, even if it's the global default for this field's + * \c PhysicalQuantity. There might not be an old \c RelativeScale though, hence the \c std::optional. + * + * .:TODO:. Fix this comment and/or the code + * Note that we are OK to use std::optional here as, per https://doc.qt.io/qt-5/signalsandslots.html, "Signals + * and slots can take any number of arguments of any type. They are completely type safe." HOWEVER, when + * referring to the function signature in .ui files, we need to remember to escape '<' to "<" and '>' to + * ">" because .ui files are XML. + */ + void changedSystemOfMeasurementOrScale(PreviousScaleInfo previousScaleInfo); + +// Using protected instead of private allows me to not use the friends +// declaration +protected: + BtFieldType fieldType; + QString propertyName; + QString configSection; + QWidget *btParent; + QMenu* contextMenu; + + void initializeSection(); + void initializeProperty(); + void initializeMenu(); + +}; + +#endif diff --git a/src/widgets/SmartLineEdit.cpp b/src/widgets/SmartLineEdit.cpp index 24ea9891..4bbfffb5 100644 --- a/src/widgets/SmartLineEdit.cpp +++ b/src/widgets/SmartLineEdit.cpp @@ -22,19 +22,91 @@ =====================================================================================================================*/ #include "widgets/SmartLineEdit.h" +#include +#include +#include +#include + +#include "measurement/Measurement.h" +#include "UiAmountWithUnits.h" + +namespace { + int const min_text_size = 8; + int const max_text_size = 50; +} + // This private implementation class holds all private non-virtual members of SmartLineEdit class SmartLineEdit::impl { public: impl(SmartLineEdit & self) : - self{self}, - initialised{false} { + m_self {self}, + m_initialised {false}, + m_fieldType {NonPhysicalQuantity::String}, + m_typeInfo {nullptr}, + m_uiAmountWithUnits {nullptr}, + m_defaultPrecision {3}, + m_maximalDisplayString{"100.000 srm"} { return; } ~impl() = default; - SmartLineEdit & self; - bool initialised; + void calculateDisplaySize(QString const & maximalDisplayString) { + // + // By default, some, but not all, boxes have a min and max width of 100 pixels, but this is not wide enough on a + // high DPI display. We instead calculate width here based on font-size - but without reducing any existing + // minimum width. + // + // Unfortunately, for a QLineEdit object, calculating the width is hard because, besides the text, we need to + // allow for the width of padding and frame, which is non-trivial to discover. Eg, typically: + // marginsAroundText() and contentsMargins() both return 0 for left and right margins + // contentsRect() and frameSize() both give the same width as width() + // AFAICT, the best option is to query via pixelMetric() calls to the widget's style, but we need to check this + // works in practice on a variety of different systems. + // + QFontMetrics displayFontMetrics(this->m_self.font()); + QRect minimumTextRect = displayFontMetrics.boundingRect(maximalDisplayString); + QMargins marginsAroundText = this->m_self.textMargins(); + auto myStyle = this->m_self.style(); + // NB: 2× frame width as on left and right; same for horizontal spacing + int totalWidgetWidthForMaximalDisplayString = minimumTextRect.width() + + marginsAroundText.left() + + marginsAroundText.right() + + (2 * myStyle->pixelMetric(QStyle::PM_DefaultFrameWidth)) + + (2 * myStyle->pixelMetric(QStyle::PM_LayoutHorizontalSpacing)); + + this->m_desiredWidthInPixels = qMax(this->m_self.minimumWidth(), totalWidgetWidthForMaximalDisplayString); + return; + } + + void setDisplaySize(bool recalculate = false) { + if (recalculate) { + QString sizingString = this->m_self.text(); + + // this is a dirty bit of cheating. If we do not reset the minimum + // width, the field only ever gets bigger. This forces the resize I + // want, but only when we are instructed to force it + this->m_self.setMinimumWidth(0); + if (sizingString.length() < min_text_size) { + sizingString = QString(min_text_size,'a'); + } else if (sizingString.length() > max_text_size) { + sizingString = QString(max_text_size,'a'); + } + this->calculateDisplaySize(sizingString); + } + this->m_self.setFixedWidth(this->m_desiredWidthInPixels); + return; + } + + + SmartLineEdit & m_self; + bool m_initialised; + BtFieldType m_fieldType; + TypeInfo const * m_typeInfo; + std::unique_ptr m_uiAmountWithUnits; + int m_defaultPrecision; + QString m_maximalDisplayString; + int m_desiredWidthInPixels; }; SmartLineEdit::SmartLineEdit(QWidget * parent) : QLineEdit(parent), pimpl{std::make_unique(*this)} { @@ -43,9 +115,79 @@ SmartLineEdit::SmartLineEdit(QWidget * parent) : QLineEdit(parent), pimpl{std::m SmartLineEdit::~SmartLineEdit() = default; -void SmartLineEdit::configure(BtFieldType const fieldType, - int const defaultPrecision, - QString const & maximalDisplayString) { - // TODO Write this! +void SmartLineEdit::init(BtFieldType const fieldType, + TypeInfo const & typeInfo, + int const defaultPrecision, + QString const & maximalDisplayString) { + // It's a coding error to call this function twice on the same object, ie we should only initialise something once! + Q_ASSERT(!this->pimpl->m_initialised); + + this->pimpl->m_fieldType = fieldType; + // It's only meaningful to have a UiAmountWithUnits if we are dealing with a PhysicalQuantity + if (!std::holds_alternative(this->pimpl->m_fieldType)) { + // It's a coding error if we already created a UiAmountWithUnits + Q_ASSERT(!this->pimpl->m_uiAmountWithUnits); + + this->pimpl->m_uiAmountWithUnits = + std::make_unique(this->parentWidget(), + ConvertToPhysicalQuantities(this->pimpl->m_fieldType)); + } + this->pimpl->m_typeInfo = &typeInfo; + this->pimpl->m_defaultPrecision = defaultPrecision; + this->pimpl->m_maximalDisplayString = maximalDisplayString; + this->pimpl->m_initialised = true; + return; +} + +BtFieldType const SmartLineEdit::getFieldType() const { + Q_ASSERT(this->pimpl->m_initialised); + return this->pimpl->m_fieldType; +} + +TypeInfo const & SmartLineEdit::getTypeInfo() const { + Q_ASSERT(this->pimpl->m_initialised); + return *this->pimpl->m_typeInfo; +} + +UiAmountWithUnits const & SmartLineEdit::getUiAmountWithUnits() const { + Q_ASSERT(this->pimpl->m_initialised); + Q_ASSERT(this->pimpl->m_uiAmountWithUnits); + return *this->pimpl->m_uiAmountWithUnits; +} + +Measurement::Amount SmartLineEdit::toCanonical() const { + Q_ASSERT(this->pimpl->m_initialised); + Q_ASSERT(this->pimpl->m_uiAmountWithUnits); + return this->pimpl->m_uiAmountWithUnits->rawToCanonical(this->text()); +} + +void SmartLineEdit::setText(std::optional amount, std::optional precision) { + Q_ASSERT(this->pimpl->m_initialised); + + if (!amount) { + // What the field is measuring doesn't matter as it's not set + this->QLineEdit::setText(""); + } else if (std::holds_alternative(this->pimpl->m_fieldType)) { + // The field is not measuring a physical quantity so there are no units or unit conversions to handle + + NonPhysicalQuantity const nonPhysicalQuantity = std::get(this->pimpl->m_fieldType); + // It's a coding error if we're trying to pass a number in to a string field + Q_ASSERT(nonPhysicalQuantity != NonPhysicalQuantity::String); + + // For percentages, we'd like to show the % symbol after the number + QString symbol{""}; + if (NonPhysicalQuantity::Percentage == nonPhysicalQuantity) { + symbol = " %"; + } + + this->QLineEdit::setText( + Measurement::displayQuantity(*amount, precision.value_or(this->pimpl->m_defaultPrecision)) + symbol + ); + } else { + // The field is measuring a physical quantity + this->pimpl->m_uiAmountWithUnits->displayAmount(*amount, precision.value_or(this->pimpl->m_defaultPrecision)); + } + + this->pimpl->setDisplaySize(); return; } diff --git a/src/widgets/SmartLineEdit.h b/src/widgets/SmartLineEdit.h index a08f5874..cfe8592d 100644 --- a/src/widgets/SmartLineEdit.h +++ b/src/widgets/SmartLineEdit.h @@ -30,6 +30,8 @@ #include #include "BtFieldType.h" +#include "utils/TypeLookup.h" +#include "UiAmountWithUnits.h" /*! * \class SmartLineEdit @@ -40,7 +42,7 @@ * \c widgets/SmartLabel.h for more details on the relationship between the two classes. * * Typically, each \c SmartLineEdit and \c SmartLabel instance are declared in a dialog's Qt Designer UI File - * (\c ui/hopEditor.ui). After it is constructed, the needs to be configured via \c SmartLineEdit::configure. + * (\c ui/hopEditor.ui). After it is constructed, the needs to be configured via \c SmartLineEdit::init. * * This two-step set-up is needed because AFAIK there is no way to pass constructor parameters to an object in a * .ui file. (If you want to do that, the advice seems to be to build the layout manually in C++ code.) @@ -67,11 +69,72 @@ class SmartLineEdit : public QLineEdit { virtual ~SmartLineEdit(); /** - * \brief TODO write this! + * \brief This needs to be called before the object is used, typically in constructor of whatever editor is using the + * widget. + * \param fieldType Tells us what \c PhysicalQuantity (or non-physical quantity such as + * \c NonPhysicalQuantity::Date, \c NonPhysicalQuantity::String, etc) this field holds + * \param typeInfo Tells us what data type we use to store the contents of the field (when converted to + * canonical units if it is a \c PhysicalQuantity) and, whether this is an optional + * field (in which case we need to handle blank / empty string as a valid value). + * \param defaultPrecision Where relevant determines the number of decimal places to show + * \param maximalDisplayString Used for determining the width of the widget */ - void configure(BtFieldType const fieldType = NonPhysicalQuantity::String, - int const defaultPrecision = 3, - QString const & maximalDisplayString = "100.000 srm"); + void init(BtFieldType const fieldType, + TypeInfo const & typeInfo, + int const defaultPrecision = 3, + QString const & maximalDisplayString = "100.000 srm"); + + BtFieldType const getFieldType() const; + + TypeInfo const & getTypeInfo() const; + + /** + * \brief If our field type is \b not \c NonPhysicalQuantity, then this returns the \c UiAmountWithUnits for handling + * units. (It is a coding error to call this function if our field type \c is \c NonPhysicalQuantity.) + */ + UiAmountWithUnits const & getUiAmountWithUnits() const; + + /** + * \brief If our field type is \b not \c NonPhysicalQuantity, then this returns the field converted to canonical + * units for the relevant \c Measurement::PhysicalQuantity. (It is a coding error to call this function if + * our field type \c is \c NonPhysicalQuantity.) + */ + Measurement::Amount toCanonical() const; + + /** + * \brief Set the amount for a decimal field + * + * \param amount is the amount to display, but the field should be blank if this is \b std::nullopt + * \param precision is how many decimal places to show. If not specified, the default will be used. + */ + void setText(std::optional amount, std::optional precision = std::nullopt); + + /** + * \brief .:TBD:. Do we need this to be able to parse numbers out of strings, or just to set string text? + */ + void setText(QString amount, std::optional precision = std::nullopt); + + /** + * \brief Use this when you want to get the text as a number (and ignore any units or other trailling letters or + * symbols) + */ + template T getValueAs() const; + +public slots: + /** + * \brief This slot receives the \c QLineEdit::editingFinished signal + */ + void onLineChanged(); + +signals: + /** + * \brief Where we want "instant updates", this signal should be picked up by the editor or widget object using this + * input field so it can read the changed value and update the underlying data model. + * + * Where we want to defer updating the underlying data model until the user clicks "Save" etc, then this + * signal will typically be ignored. + */ + void textModified(); private: // Private implementation details - see https://herbsutter.com/gotw/_100/ diff --git a/translations/bt_ca.ts b/translations/bt_ca.ts index bdf98814..6c07cfd3 100644 --- a/translations/bt_ca.ts +++ b/translations/bt_ca.ts @@ -4322,6 +4322,13 @@ El volum final al primari és de %1. L'equip i el macerat s'han reiniciat a causa de que les temperatures del macerat no s'escalen fàcilment. Cal reiniciar l'assistent del macerat. + + SmartLabel + + Color (%1) + Color (%1) + + StyleEditor diff --git a/translations/bt_cs.ts b/translations/bt_cs.ts index 2ef1fe96..59980245 100644 --- a/translations/bt_cs.ts +++ b/translations/bt_cs.ts @@ -4254,6 +4254,13 @@ Celkový objem pro hlavní kvašení je %1. Vybavení a rmutování byly vymazány jelikož se rmutovací teploty špatně škálují. Prosím, spusťe znovu průvodce rmutováním. + + SmartLabel + + Color (%1) + + + StyleEditor diff --git a/translations/bt_de.ts b/translations/bt_de.ts index 19710a39..28667911 100644 --- a/translations/bt_de.ts +++ b/translations/bt_de.ts @@ -4290,6 +4290,13 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. Die Ausrüstung und Maische wurden zurückgesetzt weil die Maischtemperaturen sich nicht einfach anpassen lassen. Bitte führen sie den Maische-Assistenten erneut aus. + + SmartLabel + + Color (%1) + Farbe (%1) + + StyleEditor diff --git a/translations/bt_el.ts b/translations/bt_el.ts index f94c03d4..1aed55b8 100644 --- a/translations/bt_el.ts +++ b/translations/bt_el.ts @@ -4258,6 +4258,13 @@ The final volume in the primary is %1. Ο εξοπλισμός και η διαδικασία σακχαροποίησης μηδενίστηκαν εξ' αιτίας του γεγονότος ότι οι θερμοκρασίες κατά την σακχαροποίηση δεν αλλάζουν εύκολα. Χρησιμοποιήστε τον οδηγό σακχαροποίησης για νέα διαδικασία. + + SmartLabel + + Color (%1) + + + StyleEditor diff --git a/translations/bt_en.ts b/translations/bt_en.ts index 4d745683..b040d70a 100644 --- a/translations/bt_en.ts +++ b/translations/bt_en.ts @@ -3635,6 +3635,13 @@ The final volume in the primary is %1. + + SmartLabel + + Color (%1) + + + StyleEditor diff --git a/translations/bt_es.ts b/translations/bt_es.ts index e97d5fd3..33b2ff4f 100644 --- a/translations/bt_es.ts +++ b/translations/bt_es.ts @@ -4310,6 +4310,13 @@ El volumen final en el primario es %1. El equipo y la maceración se han reiniciado debido al hecho que las temperaturas de la maceración no se escalan facilmente. Por favor reiniciar el asistente de la maceración. + + SmartLabel + + Color (%1) + Color (%1) + + StyleEditor diff --git a/translations/bt_et.ts b/translations/bt_et.ts index 5467072e..f9594b17 100644 --- a/translations/bt_et.ts +++ b/translations/bt_et.ts @@ -3686,6 +3686,13 @@ The final volume in the primary is %1. + + SmartLabel + + Color (%1) + + + StyleEditor diff --git a/translations/bt_eu.ts b/translations/bt_eu.ts index bbe9bd3c..10f6ed9c 100644 --- a/translations/bt_eu.ts +++ b/translations/bt_eu.ts @@ -3698,6 +3698,13 @@ The final volume in the primary is %1. + + SmartLabel + + Color (%1) + + + StyleEditor diff --git a/translations/bt_fr.ts b/translations/bt_fr.ts index 687416d0..bf59208a 100644 --- a/translations/bt_fr.ts +++ b/translations/bt_fr.ts @@ -4326,6 +4326,13 @@ Le volume final dans la cuve de fermentation est de %1. L'équipement et l'empâtage ont été réinitialisés car la température d'empâtage est difficile à adapter. Faites à nouveau appel à l'assistant d'empâtage. + + SmartLabel + + Color (%1) + Couleur (%1) + + StyleEditor diff --git a/translations/bt_gl.ts b/translations/bt_gl.ts index fe387961..884ea60c 100644 --- a/translations/bt_gl.ts +++ b/translations/bt_gl.ts @@ -3813,6 +3813,13 @@ The final volume in the primary is %1. + + SmartLabel + + Color (%1) + + + StyleEditor diff --git a/translations/bt_hu.ts b/translations/bt_hu.ts index 7bfe7e0b..b5703f1a 100644 --- a/translations/bt_hu.ts +++ b/translations/bt_hu.ts @@ -4294,6 +4294,13 @@ Végleges mennyiség az elsődleges erjesztőben: %1 Nem arányítható felszerelés és hőmérsékleti adatok. Indítsd újra a cefrézés varázslót! + + SmartLabel + + Color (%1) + Szín (%1) + + StyleEditor diff --git a/translations/bt_it.ts b/translations/bt_it.ts index a5ef7d7c..f70aa33c 100644 --- a/translations/bt_it.ts +++ b/translations/bt_it.ts @@ -4326,6 +4326,13 @@ Il Volume finale del primo è %1. Le attrezzature e il mash sono state azzerate a causa del fatto che le temperature non si sono abbassate facilmente. Si prega di eseguire nuovamente la procedura guidata di mosto. + + SmartLabel + + Color (%1) + Colore (%1) + + StyleEditor diff --git a/translations/bt_lv.ts b/translations/bt_lv.ts index 81d5f888..5290126e 100644 --- a/translations/bt_lv.ts +++ b/translations/bt_lv.ts @@ -3753,6 +3753,13 @@ The final volume in the primary is %1. + + SmartLabel + + Color (%1) + + + StyleEditor diff --git a/translations/bt_nb.ts b/translations/bt_nb.ts index 8e37bcc2..71cc765f 100644 --- a/translations/bt_nb.ts +++ b/translations/bt_nb.ts @@ -4254,6 +4254,13 @@ Sluttvolumet i primærgjæringskaret er %1. Utstyret og mesken har blitt resatt fordi mesketemperaturen ikke lar seg skalere enkelt. Vær vennlig og kjør meskeveillederen på nytt. + + SmartLabel + + Color (%1) + + + StyleEditor diff --git a/translations/bt_nl.ts b/translations/bt_nl.ts index 2fc7321f..27887c26 100644 --- a/translations/bt_nl.ts +++ b/translations/bt_nl.ts @@ -4328,6 +4328,13 @@ Het uiteindelijke volume in de hoofdvergisting is %1. Deapparatuur en maisch zijn gereset omdat de maisch temperatuur niet eenvoudig schaalt. Draai de maisch wizard opnieuw a.u.b. + + SmartLabel + + Color (%1) + Kleur (%1) + + StyleEditor diff --git a/translations/bt_pl.ts b/translations/bt_pl.ts index 9aae86a6..9d20237e 100644 --- a/translations/bt_pl.ts +++ b/translations/bt_pl.ts @@ -4254,6 +4254,13 @@ Końcowa pojemność w fermentorze wyniesie %1. Ustawienia sprzętu i zacierania zostały zresetowane z uwagi na fakt, że skalowanie temeratury zacieru jest trudne. Proszę ponownie uruchomić kreator zacierania. + + SmartLabel + + Color (%1) + + + StyleEditor diff --git a/translations/bt_pt.ts b/translations/bt_pt.ts index a5412c65..01ae4ad0 100644 --- a/translations/bt_pt.ts +++ b/translations/bt_pt.ts @@ -4274,6 +4274,13 @@ O volume final do fermentador primário é %1. O equipamento e esquema de mosturação devem ser redefinidos devido à dificuldade de dimensionar as temperaturas de mosturação. Por favor, execute novamente o assistente de mosturação. + + SmartLabel + + Color (%1) + Cor (%1) + + StyleEditor diff --git a/translations/bt_ru.ts b/translations/bt_ru.ts index 83035211..203bcb31 100644 --- a/translations/bt_ru.ts +++ b/translations/bt_ru.ts @@ -4306,6 +4306,13 @@ The final volume in the primary is %1. Оборудование и затор были сброшены т.к. температура затора не маштабируется. Пожалуйсто перезапустите визард затора. + + SmartLabel + + Color (%1) + Цвет (%1) + + StyleEditor diff --git a/translations/bt_sr.ts b/translations/bt_sr.ts index d370d559..a5a5aadc 100644 --- a/translations/bt_sr.ts +++ b/translations/bt_sr.ts @@ -4127,6 +4127,13 @@ The final volume in the primary is %1. + + SmartLabel + + Color (%1) + + + StyleEditor diff --git a/translations/bt_sv.ts b/translations/bt_sv.ts index 1e17841d..6114abcc 100644 --- a/translations/bt_sv.ts +++ b/translations/bt_sv.ts @@ -4318,6 +4318,13 @@ Primärens slutgiltiga volym är %1. Utrustningen och mäsken har återställts på grund av att mäskens temperatur inte enkelt går att skala, vänligen kör Skalningsguiden igen. + + SmartLabel + + Color (%1) + Färg (%1) + + StyleEditor diff --git a/translations/bt_tr.ts b/translations/bt_tr.ts index ba49430f..173e2a22 100644 --- a/translations/bt_tr.ts +++ b/translations/bt_tr.ts @@ -3694,6 +3694,13 @@ The final volume in the primary is %1. + + SmartLabel + + Color (%1) + + + StyleEditor diff --git a/translations/bt_zh.ts b/translations/bt_zh.ts index fce15fb8..14d4b0b3 100644 --- a/translations/bt_zh.ts +++ b/translations/bt_zh.ts @@ -4222,6 +4222,13 @@ The final volume in the primary is %1. 设备和土豆泥已复位由于这样的事实醪的温度不容易扩展。请重新运行醪向导。 + + SmartLabel + + Color (%1) + + + StyleEditor From 1812d7eedef74eda234054b52a28baa3c1d499a3 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Tue, 28 Mar 2023 08:45:44 +0200 Subject: [PATCH 03/42] Initial POC for SmartLineEdit and SmartLabel --- meson.build | 1 + src/HydrometerTool.cpp | 2 +- src/MiscEditor.cpp | 86 ++++++------- src/MiscEditor.h | 14 +-- src/UiAmountWithUnits.cpp | 6 +- src/UiAmountWithUnits.h | 9 ++ src/database/ObjectStore.cpp | 14 ++- src/measurement/PhysicalQuantity.h | 2 +- src/widgets/SmartLabel.cpp | 146 ++++++++++++++-------- src/widgets/SmartLabel.h | 22 ++-- src/widgets/SmartLineEdit.cpp | 191 +++++++++++++++++++++++++---- src/widgets/SmartLineEdit.h | 73 +++++++++-- ui/miscEditor.ui | 90 ++------------ 13 files changed, 411 insertions(+), 245 deletions(-) diff --git a/meson.build b/meson.build index d56d8e6c..f877533f 100644 --- a/meson.build +++ b/meson.build @@ -867,6 +867,7 @@ mocHeaders = files([ 'src/widgets/BtDigitWidget.h', 'src/widgets/SelectionControl.h', 'src/widgets/SmartLabel.h', + 'src/widgets/SmartLineEdit.h', 'src/widgets/ToggleSwitch.h', 'src/YeastDialog.h', 'src/YeastEditor.h', diff --git a/src/HydrometerTool.cpp b/src/HydrometerTool.cpp index e87c1252..34c85922 100644 --- a/src/HydrometerTool.cpp +++ b/src/HydrometerTool.cpp @@ -50,7 +50,7 @@ void HydrometerTool::doLayout() { QHBoxLayout* hLayout = new QHBoxLayout(this); QFormLayout* formLayout = new QFormLayout(); groupBox_inputSg = new QGroupBox(this); - groupBox_inputSg->setProperty("configSection", QVariant(QStringLiteral("hydrometerTool"))); + groupBox_inputSg->setProperty(*PropertyNames::UiAmountWithUnits::configSection, QVariant(QStringLiteral("hydrometerTool"))); label_inputSg = new BtDensityLabel(groupBox_inputSg); label_inputSg ->setContextMenuPolicy(Qt::CustomContextMenu); diff --git a/src/MiscEditor.cpp b/src/MiscEditor.cpp index 21be4328..8080176b 100644 --- a/src/MiscEditor.cpp +++ b/src/MiscEditor.cpp @@ -36,13 +36,26 @@ MiscEditor::MiscEditor(QWidget * parent) : tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs); - connect(pushButton_new, SIGNAL(clicked()), this, SLOT(newMisc())); - connect(pushButton_save, &QAbstractButton::clicked, this, &MiscEditor::save); - connect(pushButton_cancel, &QAbstractButton::clicked, this, &MiscEditor::clearAndClose); + this->lineEdit_name ->init(NonPhysicalQuantity::String , Misc::typeLookup.getType(PropertyNames::NamedEntity::name) ); + this->lineEdit_inventory->init(Measurement::PqEitherMassOrVolume , Misc::typeLookup.getType(PropertyNames::Misc::amount ), *this->label_inventory); + this->lineEdit_time ->init(Measurement::PhysicalQuantity::Time, Misc::typeLookup.getType(PropertyNames::Misc::time ), *this->label_time ); + + // Note, per https://wiki.qt.io/New_Signal_Slot_Syntax#Default_arguments_in_slot, the use of a trivial lambda + // function to allow use of default argument on newStyle() slot + connect(pushButton_new , &QAbstractButton::clicked, this, [this]() { this->newMisc(); return; } ); + connect(pushButton_save, &QAbstractButton::clicked, this, &MiscEditor::save ); + connect(pushButton_cancel, &QAbstractButton::clicked, this, &MiscEditor::clearAndClose ); + connect(checkBox_isWeight, &QCheckBox::toggled, this, &MiscEditor::setIsWeight ); + +/// // .:TBD:. Since these are buddies, you might think we could automate this... +/// connect(label_time, &SmartLabel::changedSystemOfMeasurementOrScale, lineEdit_time, &SmartLineEdit::lineChanged); +/// connect(label_inventory, &SmartLabel::changedSystemOfMeasurementOrScale, lineEdit_inventory, &SmartLineEdit::lineChanged); return; } +MiscEditor::~MiscEditor() = default; + void MiscEditor::setMisc(Misc * m) { if (obsMisc) { disconnect(obsMisc, nullptr, this, nullptr); @@ -117,49 +130,14 @@ void MiscEditor::showChanges(QMetaProperty * metaProp) { return; } } - if (propName == PropertyNames::Misc::type || updateAll) { - comboBox_type->setCurrentIndex(static_cast(obsMisc->type())); - if (!updateAll) { - return; - } - } - if (propName == PropertyNames::Misc::use || updateAll) { - comboBox_use->setCurrentIndex(static_cast(obsMisc->use())); - if (!updateAll) { - return; - } - } - if (propName == PropertyNames::Misc::time || updateAll) { - lineEdit_time->setText(obsMisc); - if (!updateAll) { - return; - } - } - if (propName == PropertyNames::Misc::amountIsWeight || updateAll) { - qDebug() << Q_FUNC_INFO; - checkBox_isWeight->setCheckState(obsMisc->amountIsWeight() ? Qt::Checked : Qt::Unchecked); - if (!updateAll) { - return; - } - } - if (propName == PropertyNames::NamedEntityWithInventory::inventory || updateAll) { - lineEdit_inventory->setText(obsMisc); - if (!updateAll) { - return; - } - } - if (propName == PropertyNames::Misc::useFor || updateAll) { - textEdit_useFor->setPlainText(obsMisc->useFor()); - if (!updateAll) { - return; - } - } - if (propName == PropertyNames::Misc::notes || updateAll) { - textEdit_notes->setPlainText(obsMisc->notes()); - if (!updateAll) { - return; - } - } + if (updateAll || propName == PropertyNames::Misc::type ) { this->comboBox_type ->setCurrentIndex(static_cast(this->obsMisc->type())); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Misc::use ) { this->comboBox_use ->setCurrentIndex(static_cast(this->obsMisc->use()) ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Misc::time ) { this->lineEdit_time ->setAmount (obsMisc->time() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Misc::amountIsWeight ) { this->checkBox_isWeight ->setCheckState (obsMisc->amountIsWeight() ? // Continues to next line + Qt::Checked : Qt::Unchecked ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::NamedEntityWithInventory::inventory) { this->lineEdit_inventory->setAmount (obsMisc->inventory() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Misc::useFor ) { this->textEdit_useFor ->setPlainText (obsMisc->useFor() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Misc::notes ) { this->textEdit_notes ->setPlainText (obsMisc->notes() ); if (!updateAll) { return; } } } void MiscEditor::newMisc(QString folder) { @@ -180,7 +158,19 @@ void MiscEditor::newMisc(QString folder) { return; } -void MiscEditor::newMisc() { - newMisc(QString()); +void MiscEditor::setIsWeight(bool const state) { + qDebug() << Q_FUNC_INFO << "state is" << state; + // But you have to admit, this is clever + this->lineEdit_inventory->getUiAmountWithUnits().selectPhysicalQuantity( + state ? Measurement::PhysicalQuantity::Mass : Measurement::PhysicalQuantity::Volume + ); + + // maybe? My head hurts now + this->lineEdit_inventory->onLineChanged(); + + // Strictly, if we change a Misc to be measured by mass instead of volume (or vice versa) we should also somehow tell + // any other bit of the UI that is showing that Misc (eg a RecipeEditor or MainWindow) to redisplay the relevant + // field. Currently we don't do this, on the assumption that it's rare you will change how a Misc is measured after + // you started using it in recipes. return; } diff --git a/src/MiscEditor.h b/src/MiscEditor.h index 54e9ca88..0bef9135 100644 --- a/src/MiscEditor.h +++ b/src/MiscEditor.h @@ -20,8 +20,9 @@ #define MISCEDITOR_H #pragma once -#include #include "ui_miscEditor.h" + +#include #include #include @@ -33,17 +34,15 @@ class Misc; * * \brief View/controller dialog for editing miscs. */ -class MiscEditor : public QDialog, private Ui::miscEditor -{ +class MiscEditor : public QDialog, private Ui::miscEditor { Q_OBJECT public: MiscEditor( QWidget *parent=nullptr ); - virtual ~MiscEditor() {} + virtual ~MiscEditor(); //! Set the misc we wish to view/edit. void setMisc( Misc* m ); //! Create a misc with folders - void newMisc(QString folder); public slots: //! Save changes. @@ -51,8 +50,9 @@ public slots: //! Clear dialog and close. void clearAndClose(); //! Add a new misc - void newMisc(); - void changed(QMetaProperty,QVariant); + void newMisc(QString folder = ""); + void changed(QMetaProperty, QVariant); + void setIsWeight(bool state); private: Misc* obsMisc; diff --git a/src/UiAmountWithUnits.cpp b/src/UiAmountWithUnits.cpp index 0d2a6c68..5c04cf2c 100644 --- a/src/UiAmountWithUnits.cpp +++ b/src/UiAmountWithUnits.cpp @@ -192,7 +192,8 @@ void UiAmountWithUnits::setConfigSection(QString configSection) { // The cascade looks a little odd, but it is intentional. this->pimpl->m_configSection = configSection; if (this->pimpl->m_configSection.isEmpty()) { - this->pimpl->m_configSection = this->pimpl->m_parent->property("configSection").toString(); + this->pimpl->m_configSection = + this->pimpl->m_parent->property(*PropertyNames::UiAmountWithUnits::configSection).toString(); } if (this->pimpl->m_configSection.isEmpty()) { this->pimpl->m_configSection = this->pimpl->m_parent->objectName(); @@ -256,7 +257,8 @@ QString UiAmountWithUnits::correctEnteredText(QString const & enteredText, Previ } correctedText = this->displayAmount(amountAsCanonical.quantity(), precision); qDebug() << - Q_FUNC_INFO << "Interpreted" << enteredText << "as" << amountAsCanonical << "and corrected to" << correctedText; + Q_FUNC_INFO << "Interpreted" << enteredText << "as" << amountAsCanonical << "and corrected to" << correctedText << + "(Edit Field =" << this->pimpl->m_editField << "Config Section =" << this->pimpl->m_configSection << ")"; return correctedText; } diff --git a/src/UiAmountWithUnits.h b/src/UiAmountWithUnits.h index 78224862..1e408bdd 100644 --- a/src/UiAmountWithUnits.h +++ b/src/UiAmountWithUnits.h @@ -31,6 +31,15 @@ #include "measurement/PhysicalQuantity.h" #include "measurement/Unit.h" #include "measurement/UnitSystem.h" +#include "utils/BtStringConst.h" + +//====================================================================================================================== +//========================================== Start of property name constants ========================================== +#define AddPropertyName(property) namespace PropertyNames::UiAmountWithUnits { BtStringConst const property{#property}; } +AddPropertyName(configSection) +#undef AddPropertyName +//=========================================== End of property name constants =========================================== +//====================================================================================================================== class QWidget; diff --git a/src/database/ObjectStore.cpp b/src/database/ObjectStore.cpp index cb7ce9bc..5444d6b6 100644 --- a/src/database/ObjectStore.cpp +++ b/src/database/ObjectStore.cpp @@ -679,12 +679,14 @@ class ObjectStore::impl { TableColumnAndType tableColumnAndType{*primaryTable.tableName, *fieldDefn.columnName, fieldDefn.fieldType}; if (legacyBadTypes.contains(tableColumnAndType) && legacyBadTypes.value(tableColumnAndType).contains(propertyType)) { - // It's technically wrong but we know about it and it works, so just log a warning - qWarning() << - Q_FUNC_INFO << fieldDefn.fieldType << "property" << fieldDefn.propertyName << "on table" << - primaryTable.tableName << "(value " << propertyValue << ") is stored as " << - propertyValue.typeName() << "(" << propertyType << ") in column" << fieldDefn.columnName << - ". This is a known ugliness that we intend to fix one day."; + // It's technically wrong but we know about it and it works, so just log it. If this logging is + // uncommented, you can get a list of all the things we need to fix with: + // grep "known ugliness" *.log | sed 's/^.*property /Property /; s/This is a known ugliness .*$//' | sort -u +/// qDebug() << +/// Q_FUNC_INFO << fieldDefn.fieldType << "property" << fieldDefn.propertyName << "on table" << +/// primaryTable.tableName << "(value " << propertyValue << ") is stored as " << +/// propertyValue.typeName() << "(" << propertyType << ") in column" << fieldDefn.columnName << +/// ". This is a known ugliness that we intend to fix one day."; } else { // It's not a known exception, so it's a coding error qCritical() << diff --git a/src/measurement/PhysicalQuantity.h b/src/measurement/PhysicalQuantity.h index f1a4fbc0..2d6f3f8f 100644 --- a/src/measurement/PhysicalQuantity.h +++ b/src/measurement/PhysicalQuantity.h @@ -95,7 +95,7 @@ namespace Measurement { Mass, // Elsewhere we use weight instead of mass because it's more idiomatic (despite being, // strictly speaking, not the same thing) Volume, - Time, // Note this is durations of time, NOT dates or times of day + Time, // Note this is durations of time, NOT dates or times of day .:TBD:. Rename to TimeDuration Temperature, Color, // Density is sometimes referred to as "gravity" as a shorthand for "specific gravity". Strictly, what we're diff --git a/src/widgets/SmartLabel.cpp b/src/widgets/SmartLabel.cpp index 66f802c4..6dc5d1ad 100644 --- a/src/widgets/SmartLabel.cpp +++ b/src/widgets/SmartLabel.cpp @@ -29,12 +29,34 @@ #include "model/Recipe.h" #include "PersistentSettings.h" #include "utils/OptionalHelpers.h" +#include "widgets/SmartLineEdit.h" #include "widgets/UnitAndScalePopUpMenu.h" -SmartLabel::SmartLabel(QWidget *parent) : +// This private implementation class holds all private non-virtual members of SmartLabel +class SmartLabel::impl { +public: + impl(SmartLabel & self, + QWidget * parent) : + m_self {self }, + m_propertyName {"" }, + m_configSection{"" }, + m_parent {parent }, + m_contextMenu {nullptr} { + return; + } + + ~impl() = default; + + SmartLabel & m_self; + QString m_propertyName; + QString m_configSection; + QWidget * m_parent; + QMenu * m_contextMenu; +}; + +SmartLabel::SmartLabel(QWidget * parent) : QLabel{parent}, - btParent{parent}, - contextMenu{nullptr} { + pimpl{std::make_unique(*this, parent)} { connect(this, &QWidget::customContextMenuRequested, this, &SmartLabel::popContextMenu); return; } @@ -76,44 +98,52 @@ void SmartLabel::textEffect(bool enabled) { } void SmartLabel::initializeSection() { - if (!this->configSection.isEmpty()) { + if (!this->pimpl->m_configSection.isEmpty()) { + // We're already initialised return; } - // as much as I dislike it, dynamic properties can't be referenced on - // initialization. - QWidget * mybuddy = this->buddy(); // - // If the label has the configSection defined, use it - // otherwise, if the paired field has a configSection, use it - // otherwise, if the parent object has a configSection, use it + // If the label has the pimpl->m_configSection defined, use it + // otherwise, if the paired field has a pimpl->m_configSection, use it + // otherwise, if the parent object has a pimpl->m_configSection, use it // if all else fails, get the parent's object name // - if (this->property("configSection").isValid()) { - this->configSection = property("configSection").toString(); - } else if (mybuddy && mybuddy->property("configSection").isValid() ) { - this->configSection = mybuddy->property("configSection").toString(); - } else if (this->btParent->property("configSection").isValid() ) { - this->configSection = this->btParent->property("configSection").toString(); - } else { - qWarning() << Q_FUNC_INFO << "this failed" << this; - this->configSection = this->btParent->objectName(); + if (this->property(*PropertyNames::UiAmountWithUnits::configSection).isValid()) { + this->pimpl->m_configSection = this->property(*PropertyNames::UiAmountWithUnits::configSection).toString(); + return; } + + // As much as I dislike it, dynamic properties can't be referenced on initialization. + QWidget const * mybuddy = this->buddy(); + if (mybuddy && mybuddy->property(*PropertyNames::UiAmountWithUnits::configSection).isValid() ) { + this->pimpl->m_configSection = mybuddy->property(*PropertyNames::UiAmountWithUnits::configSection).toString(); + return; + } + + if (this->pimpl->m_parent->property(*PropertyNames::UiAmountWithUnits::configSection).isValid() ) { + this->pimpl->m_configSection = + this->pimpl->m_parent->property(*PropertyNames::UiAmountWithUnits::configSection).toString(); + return; + } + + qWarning() << Q_FUNC_INFO << "this failed" << this; + this->pimpl->m_configSection = this->pimpl->m_parent->objectName(); return; } void SmartLabel::initializeProperty() { - if (!this->propertyName.isEmpty()) { + if (!this->pimpl->m_propertyName.isEmpty()) { return; } QWidget* mybuddy = this->buddy(); if (this->property("editField").isValid()) { - this->propertyName = this->property("editField").toString(); + this->pimpl->m_propertyName = this->property("editField").toString(); } else if (mybuddy && mybuddy->property("editField").isValid()) { - this->propertyName = mybuddy->property("editField").toString(); + this->pimpl->m_propertyName = mybuddy->property("editField").toString(); } else { qWarning() << Q_FUNC_INFO << "That failed miserably"; } @@ -121,21 +151,23 @@ void SmartLabel::initializeProperty() { } void SmartLabel::initializeMenu() { + // TODO: Change this to a smart pointer + // // If a context menu already exists, we need to delete it and recreate it. We can't always reuse an existing menu // because the sub-menu for relative scale needs to change when a different unit system is selected. (In theory we // could only recreate the context menu when a different unit system is selected, but that adds complication.) - if (this->contextMenu) { - // NB: Although the existing menu is "owned" by this->btParent, it is fine for us to delete it here. The Qt - // ownership in this context merely guarantees that this->btParent will, in its own destructor, delete the menu if + if (this->pimpl->m_contextMenu) { + // NB: Although the existing menu is "owned" by this->pimpl->m_parent, it is fine for us to delete it here. The Qt + // ownership in this context merely guarantees that this->pimpl->m_parent will, in its own destructor, delete the menu if // it still exists. - delete this->contextMenu; - this->contextMenu = nullptr; + delete this->pimpl->m_contextMenu; + this->pimpl->m_contextMenu = nullptr; } std::optional forcedSystemOfMeasurement = - Measurement::getForcedSystemOfMeasurementForField(this->propertyName, this->configSection); + Measurement::getForcedSystemOfMeasurementForField(this->pimpl->m_propertyName, this->pimpl->m_configSection); std::optional forcedRelativeScale = - Measurement::getForcedRelativeScaleForField(this->propertyName, this->configSection); + Measurement::getForcedRelativeScaleForField(this->pimpl->m_propertyName, this->pimpl->m_configSection); qDebug() << Q_FUNC_INFO << "forcedSystemOfMeasurement=" << forcedSystemOfMeasurement << ", forcedRelativeScale=" << forcedRelativeScale; @@ -148,10 +180,10 @@ void SmartLabel::initializeMenu() { // Since fieldType is not NonPhysicalQuantity this cast should be safe Measurement::PhysicalQuantity const physicalQuantity = buddy.getUiAmountWithUnits().getPhysicalQuantity(); - this->contextMenu = UnitAndScalePopUpMenu::create(this->btParent, - physicalQuantity, - forcedSystemOfMeasurement, - forcedRelativeScale); + this->pimpl->m_contextMenu = UnitAndScalePopUpMenu::create(this->pimpl->m_parent, + physicalQuantity, + forcedSystemOfMeasurement, + forcedRelativeScale); return; } @@ -159,11 +191,12 @@ void SmartLabel::popContextMenu(const QPoint& point) { // For the moment, at least, we do not allow people to choose date formats per-field. (Although you might want to // mix and match metric and imperial systems in certain circumstances, it's less clear that there's a benefit to // mixing and matching date formats.) - if (!std::holds_alternative(this->fieldType)) { + auto const fieldType = this->getBuddy().getFieldType(); + if (!std::holds_alternative(fieldType)) { return; } - QObject* calledBy = sender(); + QObject * calledBy = this->sender(); if (calledBy == nullptr) { return; } @@ -178,36 +211,40 @@ void SmartLabel::popContextMenu(const QPoint& point) { this->initializeMenu(); // Show the pop-up menu and get back whatever the user seleted - QAction * invoked = this->contextMenu->exec(widgie->mapToGlobal(point)); + QAction * invoked = this->pimpl->m_contextMenu->exec(widgie->mapToGlobal(point)); if (invoked == nullptr) { return; } // Save the current settings (which may come from system-wide defaults) for the signal below - Q_ASSERT(std::holds_alternative(this->fieldType)); - Measurement::PhysicalQuantity physicalQuantity = std::get(this->fieldType); + Q_ASSERT(std::holds_alternative(fieldType)); + Measurement::PhysicalQuantity physicalQuantity = std::get(fieldType); PreviousScaleInfo previousScaleInfo{ - Measurement::getSystemOfMeasurementForField(this->propertyName, this->configSection, physicalQuantity), - Measurement::getForcedRelativeScaleForField(this->propertyName, this->configSection) + Measurement::getSystemOfMeasurementForField(this->pimpl->m_propertyName, this->pimpl->m_configSection, physicalQuantity), + Measurement::getForcedRelativeScaleForField(this->pimpl->m_propertyName, this->pimpl->m_configSection) }; + qDebug() << + Q_FUNC_INFO << "Property Name:" << this->pimpl->m_propertyName << ", Config Section" << + this->pimpl->m_configSection; + // To make this all work, we need to set ogMin and ogMax when og is set etc QVector fieldsToSet; - fieldsToSet.append(this->propertyName); - if (this->propertyName == "og") { + fieldsToSet.append(this->pimpl->m_propertyName); + if (this->pimpl->m_propertyName == "og") { fieldsToSet.append(QString(*PropertyNames::Style::ogMin)); fieldsToSet.append(QString(*PropertyNames::Style::ogMax)); - } else if (this->propertyName == "fg") { + } else if (this->pimpl->m_propertyName == "fg") { fieldsToSet.append(QString(*PropertyNames::Style::fgMin)); fieldsToSet.append(QString(*PropertyNames::Style::fgMax)); - } else if (this->propertyName == "color_srm") { + } else if (this->pimpl->m_propertyName == "color_srm") { fieldsToSet.append(QString(*PropertyNames::Style::colorMin_srm)); fieldsToSet.append(QString(*PropertyNames::Style::colorMax_srm)); } // User will either have selected a SystemOfMeasurement or a UnitSystem::RelativeScale. We can know which based on // whether it's the menu or the sub-menu that it came from. - bool isTopMenu{invoked->parentWidget() == this->contextMenu}; + bool isTopMenu{invoked->parentWidget() == this->pimpl->m_contextMenu}; if (isTopMenu) { // It's the menu, so SystemOfMeasurement std::optional whatSelected = @@ -216,16 +253,16 @@ void SmartLabel::popContextMenu(const QPoint& point) { if (!whatSelected) { // Null means "Default", which means don't set a forced SystemOfMeasurement for this field for (auto field : fieldsToSet) { - Measurement::setForcedSystemOfMeasurementForField(field, this->configSection, std::nullopt); + Measurement::setForcedSystemOfMeasurementForField(field, this->pimpl->m_configSection, std::nullopt); } } else { for (auto field : fieldsToSet) { - Measurement::setForcedSystemOfMeasurementForField(field, this->configSection, *whatSelected); + Measurement::setForcedSystemOfMeasurementForField(field, this->pimpl->m_configSection, *whatSelected); } } // Choosing a forced SystemOfMeasurement resets any selection of forced RelativeScale for (auto field : fieldsToSet) { - Measurement::setForcedRelativeScaleForField(field, this->configSection, std::nullopt); + Measurement::setForcedRelativeScaleForField(field, this->pimpl->m_configSection, std::nullopt); } // @@ -234,11 +271,12 @@ void SmartLabel::popContextMenu(const QPoint& point) { // Assert that we already bailed above for fields that aren't a PhysicalQuantity, so we know std::get won't throw // here. // - Q_ASSERT(std::holds_alternative(this->fieldType)); - if (Measurement::PhysicalQuantity::Color == std::get(this->fieldType)) { + auto const fieldType = this->getBuddy().getFieldType(); + Q_ASSERT(std::holds_alternative(fieldType)); + if (Measurement::PhysicalQuantity::Color == std::get(fieldType)) { Measurement::UnitSystem const & disp = - Measurement::getUnitSystemForField(this->propertyName, - this->configSection, + Measurement::getUnitSystemForField(this->pimpl->m_propertyName, + this->pimpl->m_configSection, Measurement::PhysicalQuantity::Color); this->setText(tr("Color (%1)").arg(disp.unit()->name)); } @@ -250,11 +288,11 @@ void SmartLabel::popContextMenu(const QPoint& point) { if (!whatSelected) { // Null means "Default", which means don't set a forced RelativeScale for this field for (auto field : fieldsToSet) { - Measurement::setForcedRelativeScaleForField(field, this->configSection, std::nullopt); + Measurement::setForcedRelativeScaleForField(field, this->pimpl->m_configSection, std::nullopt); } } else { for (auto field : fieldsToSet) { - Measurement::setForcedRelativeScaleForField(field, this->configSection, *whatSelected); + Measurement::setForcedRelativeScaleForField(field, this->pimpl->m_configSection, *whatSelected); } } } diff --git a/src/widgets/SmartLabel.h b/src/widgets/SmartLabel.h index 4a8a6be2..6105bc66 100644 --- a/src/widgets/SmartLabel.h +++ b/src/widgets/SmartLabel.h @@ -20,6 +20,7 @@ #define WIDGETS_SMARTLABEL_H #pragma once +#include // For PImpl #include #include @@ -29,10 +30,11 @@ #include #include "BtFieldType.h" -#include "widgets/SmartLineEdit.h" #include "measurement/UnitSystem.h" #include "UiAmountWithUnits.h" // For PreviousScaleInfo +class SmartLineEdit; + /*! * \class SmartLabel * @@ -76,8 +78,12 @@ class SmartLabel : public QLabel { virtual ~SmartLabel(); /** - * \brief Our "buddy" should always be a \c SmartSmartLineEditLabel. This is a convenience function to get it - * without the caller having to downcast from \c QWidget etc. + * \brief Our "buddy" should always be a \c SmartLineEdit. This is a convenience function to get it without the + * caller having to downcast from \c QWidget etc. + * + * Note that the buddy relationship is not symmetric. Although it is easy to get the buddy of a \c QLabel (or + * derived class), it is not easy to go in the other direction. In other words, if you have the buddy of a + * \c QLabel, there is not built-in way in Qt to get back to the \c QLabel. */ SmartLineEdit & getBuddy() const; @@ -133,16 +139,16 @@ public slots: // Using protected instead of private allows me to not use the friends // declaration protected: - BtFieldType fieldType; - QString propertyName; - QString configSection; - QWidget *btParent; - QMenu* contextMenu; void initializeSection(); void initializeProperty(); void initializeMenu(); +private: + // Private implementation details - see https://herbsutter.com/gotw/_100/ + class impl; + std::unique_ptr pimpl; + }; #endif diff --git a/src/widgets/SmartLineEdit.cpp b/src/widgets/SmartLineEdit.cpp index 4bbfffb5..8430f85e 100644 --- a/src/widgets/SmartLineEdit.cpp +++ b/src/widgets/SmartLineEdit.cpp @@ -22,6 +22,7 @@ =====================================================================================================================*/ #include "widgets/SmartLineEdit.h" +#include #include #include #include @@ -29,6 +30,8 @@ #include "measurement/Measurement.h" #include "UiAmountWithUnits.h" +#include "utils/OptionalHelpers.h" +#include "widgets/SmartLabel.h" namespace { int const min_text_size = 8; @@ -43,9 +46,13 @@ class SmartLineEdit::impl { m_initialised {false}, m_fieldType {NonPhysicalQuantity::String}, m_typeInfo {nullptr}, + m_buddyLabel {nullptr}, m_uiAmountWithUnits {nullptr}, m_defaultPrecision {3}, - m_maximalDisplayString{"100.000 srm"} { + m_maximalDisplayString{"100.000 srm"}, + m_desiredWidthInPixels{0}, + m_editField {""}, + m_configSection {""} { return; } @@ -98,15 +105,65 @@ class SmartLineEdit::impl { return; } + /** + * \brief We want to have two different signatures of \c SmartLineEdit::init so we can catch missing parameters at + * compile time. Ultimately they both do pretty much the same work, by calling this function. + */ + void init(BtFieldType const fieldType, + TypeInfo const & typeInfo, + SmartLabel * buddyLabel, + int const defaultPrecision, + QString const & maximalDisplayString) { + // It's a coding error to call this function twice on the same object, ie we should only initialise something once! + Q_ASSERT(!this->m_initialised); + + this->m_fieldType = fieldType; + this->m_typeInfo = &typeInfo; + this->m_buddyLabel = buddyLabel; + this->m_defaultPrecision = defaultPrecision; + this->m_maximalDisplayString = maximalDisplayString; + this->m_initialised = true; + + connect(&this->m_self, &QLineEdit::editingFinished, &this->m_self, &SmartLineEdit::onLineChanged); + if (!std::holds_alternative(this->m_fieldType)) { + QString configSection = + this->m_self.property(*PropertyNames::UiAmountWithUnits::configSection).toString(); + qDebug() << Q_FUNC_INFO << "Config Section =" << configSection; + this->m_uiAmountWithUnits->setConfigSection(configSection); + + // It's a coding error if we didn't specify the buddy for something measuring a physical quantity + Q_ASSERT(this->m_buddyLabel); + + // It's also a coding error if we are not the buddy of the label we think we are. However, we cannot test this + // here as the buddy's QLabel::setBuddy() hasn't necessarily yet been called from the code generated from the + // .ui file. What we can do, as belt-and-braces is call it here. + this->m_buddyLabel->setBuddy(&this->m_self); + + connect(this->m_buddyLabel, &SmartLabel::changedSystemOfMeasurementOrScale, &this->m_self, &SmartLineEdit::lineChanged); + } + + // We can work out (and store) our display size here, but we don't yet set it. The way the Designer UI Files work is + // to generate code that calls setters such as setMaximumWidth() etc, which would override anything we do too early + // on in the life of the object. To be safe therefore, we set our size when setText() is called. + this->calculateDisplaySize(maximalDisplayString); + return; + } + SmartLineEdit & m_self; bool m_initialised; BtFieldType m_fieldType; TypeInfo const * m_typeInfo; + SmartLabel * m_buddyLabel; std::unique_ptr m_uiAmountWithUnits; int m_defaultPrecision; QString m_maximalDisplayString; int m_desiredWidthInPixels; + // .:TBD:. This is a bit ugly. We keep our own copies of fields that exist in UiAmountWithUnits because we get given + // the values before we have created the UiAmountWithUnits object + QString m_editField; + QString m_configSection; + }; SmartLineEdit::SmartLineEdit(QWidget * parent) : QLineEdit(parent), pimpl{std::make_unique(*this)} { @@ -115,27 +172,33 @@ SmartLineEdit::SmartLineEdit(QWidget * parent) : QLineEdit(parent), pimpl{std::m SmartLineEdit::~SmartLineEdit() = default; -void SmartLineEdit::init(BtFieldType const fieldType, - TypeInfo const & typeInfo, - int const defaultPrecision, - QString const & maximalDisplayString) { - // It's a coding error to call this function twice on the same object, ie we should only initialise something once! - Q_ASSERT(!this->pimpl->m_initialised); - - this->pimpl->m_fieldType = fieldType; - // It's only meaningful to have a UiAmountWithUnits if we are dealing with a PhysicalQuantity - if (!std::holds_alternative(this->pimpl->m_fieldType)) { - // It's a coding error if we already created a UiAmountWithUnits - Q_ASSERT(!this->pimpl->m_uiAmountWithUnits); - - this->pimpl->m_uiAmountWithUnits = - std::make_unique(this->parentWidget(), - ConvertToPhysicalQuantities(this->pimpl->m_fieldType)); - } - this->pimpl->m_typeInfo = &typeInfo; - this->pimpl->m_defaultPrecision = defaultPrecision; - this->pimpl->m_maximalDisplayString = maximalDisplayString; - this->pimpl->m_initialised = true; +void SmartLineEdit::init(Measurement::PhysicalQuantities const physicalQuantities, + TypeInfo const & typeInfo, + SmartLabel & buddyLabel, + int const defaultPrecision, + QString const & maximalDisplayString) { + // It's only meaningful to have a UiAmountWithUnits if we are dealing with a PhysicalQuantity, hence why we do it + // here and not in SmartLineEdit::impl::init(). + // It's a coding error if we already created a UiAmountWithUnits + Q_ASSERT(!this->pimpl->m_uiAmountWithUnits); + this->pimpl->m_uiAmountWithUnits = std::make_unique(this->parentWidget(), physicalQuantities); + // See above for why we need to pass these in to UiAmountWithUnits + if (!this->pimpl->m_editField .isEmpty()) { this->pimpl->m_uiAmountWithUnits->setEditField (this->pimpl->m_editField ); } + if (!this->pimpl->m_configSection.isEmpty()) { this->pimpl->m_uiAmountWithUnits->setConfigSection(this->pimpl->m_configSection); } + + this->pimpl->init(ConvertToBtFieldType(physicalQuantities), + typeInfo, + &buddyLabel, + defaultPrecision, + maximalDisplayString); + return; +} + +void SmartLineEdit::init(NonPhysicalQuantity const nonPhysicalQuantity, + TypeInfo const & typeInfo, + int const defaultPrecision, + QString const & maximalDisplayString) { + this->pimpl->init(nonPhysicalQuantity, typeInfo, nullptr, defaultPrecision, maximalDisplayString); return; } @@ -149,7 +212,7 @@ TypeInfo const & SmartLineEdit::getTypeInfo() const { return *this->pimpl->m_typeInfo; } -UiAmountWithUnits const & SmartLineEdit::getUiAmountWithUnits() const { +UiAmountWithUnits & SmartLineEdit::getUiAmountWithUnits() const { Q_ASSERT(this->pimpl->m_initialised); Q_ASSERT(this->pimpl->m_uiAmountWithUnits); return *this->pimpl->m_uiAmountWithUnits; @@ -161,7 +224,7 @@ Measurement::Amount SmartLineEdit::toCanonical() const { return this->pimpl->m_uiAmountWithUnits->rawToCanonical(this->text()); } -void SmartLineEdit::setText(std::optional amount, std::optional precision) { +void SmartLineEdit::setAmount(std::optional amount, std::optional precision) { Q_ASSERT(this->pimpl->m_initialised); if (!amount) { @@ -185,9 +248,87 @@ void SmartLineEdit::setText(std::optional amount, std::optional pre ); } else { // The field is measuring a physical quantity - this->pimpl->m_uiAmountWithUnits->displayAmount(*amount, precision.value_or(this->pimpl->m_defaultPrecision)); + this->QLineEdit::setText( + this->pimpl->m_uiAmountWithUnits->displayAmount(*amount, precision.value_or(this->pimpl->m_defaultPrecision)) + ); } this->pimpl->setDisplaySize(); return; } + +//============================================ Property Getters and Setters ============================================ +// Note that we cannot assume init() has yet been run when these are called from (code generated from) a .ui file + +void SmartLineEdit::setEditField (QString val) { this->pimpl->m_editField = val; if (this->pimpl->m_initialised) { this->pimpl->m_uiAmountWithUnits->setEditField (val); } return; } +void SmartLineEdit::setConfigSection (QString val) { this->pimpl->m_configSection = val; if (this->pimpl->m_initialised) { this->pimpl->m_uiAmountWithUnits->setConfigSection (val); } return; } + // TODO Check where these two are used and whether we can eliminate them +void SmartLineEdit::setForcedSystemOfMeasurementViaString(QString val) { Q_ASSERT(this->pimpl->m_initialised); this->pimpl->m_uiAmountWithUnits->setForcedSystemOfMeasurementViaString(val); return; } +void SmartLineEdit::setForcedRelativeScaleViaString (QString val) { Q_ASSERT(this->pimpl->m_initialised); this->pimpl->m_uiAmountWithUnits->setForcedRelativeScaleViaString (val); return; } + +QString SmartLineEdit::getEditField() const { if (this->pimpl->m_initialised) { return this->pimpl->m_uiAmountWithUnits->getEditField() ; } return this->pimpl->m_editField ; } +QString SmartLineEdit::getConfigSection() /* not const */ { if (this->pimpl->m_initialised) { return this->pimpl->m_uiAmountWithUnits->getConfigSection() /* not const */ ; } return this->pimpl->m_configSection; } +QString SmartLineEdit::getForcedSystemOfMeasurementViaString() const { Q_ASSERT(this->pimpl->m_initialised); return this->pimpl->m_uiAmountWithUnits->getForcedSystemOfMeasurementViaString(); } +QString SmartLineEdit::getForcedRelativeScaleViaString() const { Q_ASSERT(this->pimpl->m_initialised); return this->pimpl->m_uiAmountWithUnits->getForcedRelativeScaleViaString() ; } + +//====================================================================================================================== + +void SmartLineEdit::onLineChanged() { + Q_ASSERT(this->pimpl->m_initialised); + + if (std::holds_alternative(this->pimpl->m_fieldType)) { + // The field is not measuring a physical quantity so there are no units or unit conversions to handle + qDebug() << Q_FUNC_INFO; + if (sender() == this) { + emit textModified(); + } + return; + } + + // The field is measuring a physical quantity + Q_ASSERT(this->pimpl->m_uiAmountWithUnits); + qDebug() << + Q_FUNC_INFO << "this->fieldType=" << this->pimpl->m_fieldType << ", forcedSystemOfMeasurement=" << + this->pimpl->m_uiAmountWithUnits->getForcedSystemOfMeasurement() << ", forcedRelativeScale=" << + this->pimpl->m_uiAmountWithUnits->getForcedRelativeScale() << ", value=" << this->text(); + + Measurement::PhysicalQuantities const physicalQuantities = ConvertToPhysicalQuantities(this->pimpl->m_fieldType); + + QString const propertyName = this->pimpl->m_uiAmountWithUnits->getEditField(); + QString const configSection = this->pimpl->m_uiAmountWithUnits->getConfigSection(); + Measurement::SystemOfMeasurement const oldSystemOfMeasurement = + Measurement::getSystemOfMeasurementForField(propertyName, configSection, physicalQuantities); + auto oldForcedRelativeScale = Measurement::getForcedRelativeScaleForField(propertyName, configSection); + PreviousScaleInfo previousScaleInfo{ + oldSystemOfMeasurement, + oldForcedRelativeScale + }; + + qDebug() << + Q_FUNC_INFO << "propertyName=" << propertyName << ", configSection=" << configSection << + ", oldSystemOfMeasurement=" << oldSystemOfMeasurement << ", oldForcedRelativeScale=" << oldForcedRelativeScale; + + this->lineChanged(previousScaleInfo); + return; +} + +void SmartLineEdit::lineChanged(PreviousScaleInfo previousScaleInfo) { + Q_ASSERT(this->pimpl->m_initialised); + Q_ASSERT(this->pimpl->m_uiAmountWithUnits); + + // editingFinished happens on focus being lost, regardless of anything + // being changed. I am hoping this short circuits properly and we do + // nothing if nothing changed. + if (this->sender() == this && !this->isModified()) { + qDebug() << Q_FUNC_INFO << "Nothing changed; field holds" << this->text(); + return; + } + + this->QLineEdit::setText(this->pimpl->m_uiAmountWithUnits->correctEnteredText(this->text(), previousScaleInfo)); + + if (sender() == this) { + emit textModified(); + } + + return; +} diff --git a/src/widgets/SmartLineEdit.h b/src/widgets/SmartLineEdit.h index cfe8592d..4152b7c9 100644 --- a/src/widgets/SmartLineEdit.h +++ b/src/widgets/SmartLineEdit.h @@ -33,6 +33,8 @@ #include "utils/TypeLookup.h" #include "UiAmountWithUnits.h" +class SmartLabel; + /*! * \class SmartLineEdit * @@ -45,7 +47,9 @@ * (\c ui/hopEditor.ui). After it is constructed, the needs to be configured via \c SmartLineEdit::init. * * This two-step set-up is needed because AFAIK there is no way to pass constructor parameters to an object in a - * .ui file. (If you want to do that, the advice seems to be to build the layout manually in C++ code.) + * .ui file. (If you want to do that, the advice seems to be to build the layout manually in C++ code.) You can + * (and we do) set Qt properties from (the code generated from) a .ui file, but that means passing in everything + * as a string and losing a lot of compile-time checks. * * Similarly, we might think to template \c SmartLineEdit, but the Qt Meta-Object Compiler (moc) doesn't * understand C++ templates. This means we can't template classes that need to use the \c Q_OBJECT macro @@ -63,6 +67,15 @@ class SmartLineEdit : public QLineEdit { Q_OBJECT + // These properties are needed so that the .ui files can uniquely identify each field and so that, for fields + // relating to physical quantities, the user can individually set the system of measurement and relative scale. + // + // They all pass through to UiAmountWithUnits + Q_PROPERTY(QString configSection READ getConfigSection WRITE setConfigSection STORED false) + Q_PROPERTY(QString editField READ getEditField WRITE setEditField STORED false) + Q_PROPERTY(QString forcedSystemOfMeasurement READ getForcedSystemOfMeasurementViaString WRITE setForcedSystemOfMeasurementViaString STORED false) + Q_PROPERTY(QString forcedRelativeScale READ getForcedRelativeScaleViaString WRITE setForcedRelativeScaleViaString STORED false) + public: SmartLineEdit(QWidget* parent = nullptr); @@ -70,17 +83,30 @@ class SmartLineEdit : public QLineEdit { /** * \brief This needs to be called before the object is used, typically in constructor of whatever editor is using the - * widget. - * \param fieldType Tells us what \c PhysicalQuantity (or non-physical quantity such as - * \c NonPhysicalQuantity::Date, \c NonPhysicalQuantity::String, etc) this field holds + * widget. As well as passing in a bunch of info that cannot easily be given to the constructor (per comment + * above), it also ensures, if necessary, that the \c changedSystemOfMeasurementOrScale signal from the + * \c SmartLabel buddy is connected to the \c lineChanged slot of this \c SmartLineEdit. + * + * \param physicalQuantities Tells us what \c PhysicalQuantity (or \c Mixed2PhysicalQuantities) this field holds * \param typeInfo Tells us what data type we use to store the contents of the field (when converted to * canonical units if it is a \c PhysicalQuantity) and, whether this is an optional * field (in which case we need to handle blank / empty string as a valid value). + * \param buddyLabel Required if \c fieldType is \b not a \c NonPhysicalQuantity * \param defaultPrecision Where relevant determines the number of decimal places to show * \param maximalDisplayString Used for determining the width of the widget */ - void init(BtFieldType const fieldType, - TypeInfo const & typeInfo, + void init(Measurement::PhysicalQuantities const physicalQuantities, + TypeInfo const & typeInfo, + SmartLabel & buddyLabel, + int const defaultPrecision = 3, + QString const & maximalDisplayString = "100.000 srm"); + + /** + * \brief As above, but for non-physical quantity such as \c NonPhysicalQuantity::Date, + * \c NonPhysicalQuantity::String, etc. + */ + void init(NonPhysicalQuantity const nonPhysicalQuantity, + TypeInfo const & typeInfo, int const defaultPrecision = 3, QString const & maximalDisplayString = "100.000 srm"); @@ -92,7 +118,7 @@ class SmartLineEdit : public QLineEdit { * \brief If our field type is \b not \c NonPhysicalQuantity, then this returns the \c UiAmountWithUnits for handling * units. (It is a coding error to call this function if our field type \c is \c NonPhysicalQuantity.) */ - UiAmountWithUnits const & getUiAmountWithUnits() const; + UiAmountWithUnits & getUiAmountWithUnits() const; /** * \brief If our field type is \b not \c NonPhysicalQuantity, then this returns the field converted to canonical @@ -107,12 +133,12 @@ class SmartLineEdit : public QLineEdit { * \param amount is the amount to display, but the field should be blank if this is \b std::nullopt * \param precision is how many decimal places to show. If not specified, the default will be used. */ - void setText(std::optional amount, std::optional precision = std::nullopt); + void setAmount(std::optional amount, std::optional precision = std::nullopt); - /** - * \brief .:TBD:. Do we need this to be able to parse numbers out of strings, or just to set string text? - */ - void setText(QString amount, std::optional precision = std::nullopt); +// /** +// * \brief Set the text for a non-numeric field +// */ +// void setText(QString amount, std::optional precision = std::nullopt); /** * \brief Use this when you want to get the text as a number (and ignore any units or other trailling letters or @@ -120,12 +146,35 @@ class SmartLineEdit : public QLineEdit { */ template T getValueAs() const; + //========================================== Property Getters and Setters =========================================== + void setEditField(QString editField); + void setConfigSection(QString configSection); + void setForcedSystemOfMeasurementViaString(QString systemOfMeasurementAsString); + void setForcedRelativeScaleViaString(QString relativeScaleAsString); + + QString getEditField() const; + QString getConfigSection(); // This does lazy-loading so isn't const + QString getForcedSystemOfMeasurementViaString() const; + QString getForcedRelativeScaleViaString() const; + //=================================================================================================================== + + public slots: /** * \brief This slot receives the \c QLineEdit::editingFinished signal */ void onLineChanged(); + /** + * \brief This is called from \c onLineChanged and also directly receives the + * \c SmartLabel::changedSystemOfMeasurementOrScale signal when the user has changed units (eg from US + * Customary to Metric). + * + * In previous versions of the code, this was mostly referenced in .ui files. One disadvantage of that + * approach was that the signal connections were only checked at run-time. + */ + void lineChanged(PreviousScaleInfo previousScaleInfo); + signals: /** * \brief Where we want "instant updates", this signal should be picked up by the editor or widget object using this diff --git a/ui/miscEditor.ui b/ui/miscEditor.ui index 0beb933e..51d6752e 100644 --- a/ui/miscEditor.ui +++ b/ui/miscEditor.ui @@ -34,7 +34,7 @@ - + 100 @@ -137,7 +137,7 @@ - + 0 @@ -159,7 +159,7 @@ - + 100 @@ -248,7 +248,7 @@ - + Qt::CustomContextMenu @@ -261,7 +261,7 @@ - + 0 @@ -404,41 +404,19 @@ - BtStringEdit - QLineEdit -
BtLineEdit.h
-
- - BtTimeEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtTimeLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
- - BtMixedMassOrVolumeLabel + SmartLabel QLabel -
BtLabel.h
+
widgets/SmartLabel.h
changedSystemOfMeasurementOrScale(PreviousScaleInfo)
- BtMixedMassOrVolumeEdit + SmartLineEdit QLineEdit -
BtAmountEdit.h
+
widgets/SmartLineEdit.h
lineChanged(PreviousScaleInfo) - setIsWeight(bool)
@@ -458,54 +436,4 @@ - - - label_time - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_time - lineChanged(PreviousScaleInfo) - - - 20 - 108 - - - 186 - 108 - - - - - label_inventory - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_inventory - lineChanged(PreviousScaleInfo) - - - 108 - 195 - - - 151 - 195 - - - - - checkBox_isWeight - toggled(bool) - lineEdit_inventory - setIsWeight(bool) - - - 231 - 168 - - - 227 - 197 - - - - From debe438115d8a68b5520347ea07c5dd384ce820e Mon Sep 17 00:00:00 2001 From: Matt Young Date: Wed, 29 Mar 2023 10:59:51 +0200 Subject: [PATCH 04/42] Incorporate BtFieldType into TypeInfo --- src/BrewDayScrollWidget.cpp | 7 ++-- src/BtFieldType.h | 1 + src/MiscEditor.cpp | 10 ++--- src/model/Hop.cpp | 32 +++++++------- src/model/Misc.cpp | 6 +-- src/model/NamedEntity.cpp | 12 +++--- src/utils/TypeLookup.h | 78 ++++++++++++++++++++++++++--------- src/widgets/SmartLineEdit.cpp | 42 +++++++++---------- src/widgets/SmartLineEdit.h | 19 ++++----- 9 files changed, 119 insertions(+), 88 deletions(-) diff --git a/src/BrewDayScrollWidget.cpp b/src/BrewDayScrollWidget.cpp index 00b59ab5..9fb71420 100644 --- a/src/BrewDayScrollWidget.cpp +++ b/src/BrewDayScrollWidget.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * BrewDayScrollWidget.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * BrewDayScrollWidget.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Carles Muñoz Gorriz * • Daniel Pettersson @@ -82,6 +82,7 @@ BrewDayScrollWidget::~BrewDayScrollWidget() = default; void BrewDayScrollWidget::saveInstruction() { this->recObs->instructions()[ listWidget->currentRow() ]->setDirections( btTextEdit->toPlainText() ); + return; } void BrewDayScrollWidget::showInstruction(int insNdx) { @@ -273,7 +274,7 @@ void BrewDayScrollWidget::insertInstruction() { // After updating the model, this is the simplest way to update the display this->setRecipe(this->recObs); - listWidget->setCurrentRow(pos-1); + listWidget->setCurrentRow(pos - 1); return; } @@ -316,7 +317,7 @@ void BrewDayScrollWidget::showChanges() { return; } - repopulateListWidget(); + this->repopulateListWidget(); return; } diff --git a/src/BtFieldType.h b/src/BtFieldType.h index 2ab6b4b4..e835169e 100644 --- a/src/BtFieldType.h +++ b/src/BtFieldType.h @@ -35,6 +35,7 @@ enum class NonPhysicalQuantity { Percentage, Bool, /** + * TODO Kill this! * \brief This is for a number that has no units, not even pseudo ones. It is currently a bit over-used -- ie there * are places we are using this (typically via BtNumberOnlyEdit) where we probably should be using a * \c PhysicalQuantity. We should fix these over time. diff --git a/src/MiscEditor.cpp b/src/MiscEditor.cpp index 8080176b..784b69b1 100644 --- a/src/MiscEditor.cpp +++ b/src/MiscEditor.cpp @@ -36,9 +36,9 @@ MiscEditor::MiscEditor(QWidget * parent) : tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs); - this->lineEdit_name ->init(NonPhysicalQuantity::String , Misc::typeLookup.getType(PropertyNames::NamedEntity::name) ); - this->lineEdit_inventory->init(Measurement::PqEitherMassOrVolume , Misc::typeLookup.getType(PropertyNames::Misc::amount ), *this->label_inventory); - this->lineEdit_time ->init(Measurement::PhysicalQuantity::Time, Misc::typeLookup.getType(PropertyNames::Misc::time ), *this->label_time ); + this->lineEdit_name ->init(Misc::typeLookup.getType(PropertyNames::NamedEntity::name) ); + this->lineEdit_inventory->init(Misc::typeLookup.getType(PropertyNames::Misc::amount ), *this->label_inventory); + this->lineEdit_time ->init(Misc::typeLookup.getType(PropertyNames::Misc::time ), *this->label_time ); // Note, per https://wiki.qt.io/New_Signal_Slot_Syntax#Default_arguments_in_slot, the use of a trivial lambda // function to allow use of default argument on newStyle() slot @@ -47,10 +47,6 @@ MiscEditor::MiscEditor(QWidget * parent) : connect(pushButton_cancel, &QAbstractButton::clicked, this, &MiscEditor::clearAndClose ); connect(checkBox_isWeight, &QCheckBox::toggled, this, &MiscEditor::setIsWeight ); -/// // .:TBD:. Since these are buddies, you might think we could automate this... -/// connect(label_time, &SmartLabel::changedSystemOfMeasurementOrScale, lineEdit_time, &SmartLineEdit::lineChanged); -/// connect(label_inventory, &SmartLabel::changedSystemOfMeasurementOrScale, lineEdit_inventory, &SmartLineEdit::lineChanged); - return; } diff --git a/src/model/Hop.cpp b/src/model/Hop.cpp index bacea70b..a901e3e9 100644 --- a/src/model/Hop.cpp +++ b/src/model/Hop.cpp @@ -154,32 +154,32 @@ TypeLookup const Hop::typeLookup { PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::use , Hop::m_use ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::type , Hop::m_type ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::form , Hop::m_form ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::alpha_pct , Hop::m_alpha_pct ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::alpha_pct , Hop::m_alpha_pct , NonPhysicalQuantity::Percentage), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::amount_kg , Hop::m_amount_kg ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::time_min , Hop::m_time_min ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::notes , Hop::m_notes ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::beta_pct , Hop::m_beta_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::hsi_pct , Hop::m_hsi_pct ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::beta_pct , Hop::m_beta_pct , NonPhysicalQuantity::Percentage), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::hsi_pct , Hop::m_hsi_pct , NonPhysicalQuantity::Percentage), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::origin , Hop::m_origin ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::substitutes , Hop::m_substitutes ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::humulene_pct , Hop::m_humulene_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::caryophyllene_pct , Hop::m_caryophyllene_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::cohumulone_pct , Hop::m_cohumulone_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::myrcene_pct , Hop::m_myrcene_pct ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::humulene_pct , Hop::m_humulene_pct , NonPhysicalQuantity::Percentage), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::caryophyllene_pct , Hop::m_caryophyllene_pct , NonPhysicalQuantity::Percentage), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::cohumulone_pct , Hop::m_cohumulone_pct , NonPhysicalQuantity::Percentage), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::myrcene_pct , Hop::m_myrcene_pct , NonPhysicalQuantity::Percentage), // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::producer , Hop::m_producer ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::product_id , Hop::m_product_id ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::year , Hop::m_year ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::total_oil_ml_per_100g, Hop::m_total_oil_ml_per_100g), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::farnesene_pct , Hop::m_farnesene_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::geraniol_pct , Hop::m_geraniol_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::b_pinene_pct , Hop::m_b_pinene_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::linalool_pct , Hop::m_linalool_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::limonene_pct , Hop::m_limonene_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::nerol_pct , Hop::m_nerol_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::pinene_pct , Hop::m_pinene_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::polyphenols_pct , Hop::m_polyphenols_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::xanthohumol_pct , Hop::m_xanthohumol_pct ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::farnesene_pct , Hop::m_farnesene_pct , NonPhysicalQuantity::Percentage), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::geraniol_pct , Hop::m_geraniol_pct , NonPhysicalQuantity::Percentage), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::b_pinene_pct , Hop::m_b_pinene_pct , NonPhysicalQuantity::Percentage), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::linalool_pct , Hop::m_linalool_pct , NonPhysicalQuantity::Percentage), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::limonene_pct , Hop::m_limonene_pct , NonPhysicalQuantity::Percentage), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::nerol_pct , Hop::m_nerol_pct , NonPhysicalQuantity::Percentage), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::pinene_pct , Hop::m_pinene_pct , NonPhysicalQuantity::Percentage), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::polyphenols_pct , Hop::m_polyphenols_pct , NonPhysicalQuantity::Percentage), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::xanthohumol_pct , Hop::m_xanthohumol_pct , NonPhysicalQuantity::Percentage), }, // Parent class lookup. NB: NamedEntityWithInventory not NamedEntity! &NamedEntityWithInventory::typeLookup diff --git a/src/model/Misc.cpp b/src/model/Misc.cpp index 0a89c01f..a8343881 100644 --- a/src/model/Misc.cpp +++ b/src/model/Misc.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * model/Misc.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * model/Misc.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Mattias Måhl * • Matt Young @@ -61,11 +61,11 @@ ObjectStore & Misc::getObjectStoreTypedInstance() const { TypeLookup const Misc::typeLookup { "Misc", { - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Misc::amount , Misc::m_amount ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Misc::amount , Misc::m_amount , Measurement::PqEitherMassOrVolume), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Misc::amountIsWeight, Misc::m_amountIsWeight), // PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Misc::amountType , Misc::m_amountType ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Misc::notes , Misc::m_notes ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Misc::time , Misc::m_time ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Misc::time , Misc::m_time , Measurement::PhysicalQuantity::Time), // PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Misc::typeString , Misc::m_typeString ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Misc::type , Misc::m_type ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Misc::useFor , Misc::m_useFor ), diff --git a/src/model/NamedEntity.cpp b/src/model/NamedEntity.cpp index 75a37771..e11c8cae 100644 --- a/src/model/NamedEntity.cpp +++ b/src/model/NamedEntity.cpp @@ -103,12 +103,12 @@ TypeLookup const NamedEntity::typeLookup { // everything else out. The only exception is that, for enums, we have to pretend they are stored as int, because // that's what's going to come out of the Qt property system (and it would significantly complicate other bits of // the code to separately register every different enum that we use.) - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::NamedEntity::deleted , NamedEntity::m_display), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::NamedEntity::display , NamedEntity::m_display), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::NamedEntity::folder , NamedEntity::m_folder ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::NamedEntity::key , NamedEntity::m_key ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::NamedEntity::name , NamedEntity::m_name ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::NamedEntity::parentKey, NamedEntity::parentKey), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::NamedEntity::deleted , NamedEntity::m_display ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::NamedEntity::display , NamedEntity::m_display ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::NamedEntity::folder , NamedEntity::m_folder ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::NamedEntity::key , NamedEntity::m_key ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::NamedEntity::name , NamedEntity::m_name , NonPhysicalQuantity::String), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::NamedEntity::parentKey, NamedEntity::parentKey ), }, // Parent class lookup - none as we're top of the tree nullptr diff --git a/src/utils/TypeLookup.h b/src/utils/TypeLookup.h index da9c7499..889015df 100644 --- a/src/utils/TypeLookup.h +++ b/src/utils/TypeLookup.h @@ -22,6 +22,7 @@ #include #include +#include "BtFieldType.h" #include "utils/BtStringConst.h" /** @@ -65,6 +66,16 @@ struct is_optional_enum : public std::false_type{}; template struct is_optional_enum< std::optional > : public std::is_enum{}; +// This bit requires C++20 or later. It makes the specialisations of TypeInfo::construct() below a bit less clunky +template +concept IsRequiredEnum = std::is_enum::value; +template +concept IsRequiredOther = !std::is_enum::value && !is_optional::value; +template +concept IsOptionalEnum = is_optional_enum::value; +template +concept IsOptionalOther = !is_optional_enum::value && is_optional::value; + /** * \brief Extends \c std::type_index with some other info we need about a type for serialisation, specifically whether * it is an enum and/or whether it is \c std::optional. @@ -94,28 +105,46 @@ struct TypeInfo { }; Classification classification; + /** + * \brief Where appropriate, this tells us what is actually being stored. Eg, \c typeIndex might tells us that a + * field is a \c double and \c classification indicates whether it is wrapped in \c std::optional, but this + * is what we need to determine whether it is storing \c PhysicalQuantity::Mass (in kilograms) or + * \c PhysicalQuantity::Temperature (in Celsius) or \c NonPhysicalQuantity::Percentage, etc. + * + * This is only set for fields where it could have a meaning, eg we wouldn't set it for a foreign key field. + * + * Although we _could_ do some clever stuff to automatically deduce the value of this field in certain cases + * (eg for a \c bool type, this is probably \c NonPhysicalQuantity::Bool, for a \c QString type, this is + * probably \c NonPhysicalQuantity::String, etc), I have deliberately not done so for these reasons: + * - Having a value set here shows this is a property that we want to expose to the user. Where a property + * is for internal use only (but nonetheless stored in the DB etc), then this field should be + * \c std::nullopt + * - Things that we think can be deduced now might not always remain so. Eg, at a future date, it is at + * least conceivable that there might be some new \c NonPhysicalQuantity that we also want to store in a + * \c QString + * - Adding all the deduction logic here makes this code more complicated (and thus more liable to bugs) + * but only saves us a small amount in each 'static TypeLookup const typeLookup' definition. + */ + std::optional fieldType; + + /** + * \return \c true if \c classification is \c OptionalEnum or \c OptionalOther, \c false otherwise (ie if + * \c classification is \c RequiredEnum or \c RequiredOther + */ bool isOptional() const; /** - * \brief Factory function to construct a \c TypeInfo for a given type. - * - * TODO: We could probably do this via a bunch of template specialisations... + * \brief Factory functions to construct a \c TypeInfo for a given type. */ - template - const static TypeInfo construct() { - if (std::is_enum::value) { - return TypeInfo{typeid(T), Classification::RequiredEnum}; - } - if (!is_optional::value) { - return TypeInfo{typeid(T), Classification::RequiredOther}; - } - if (is_optional_enum::value) { - return TypeInfo{typeid(T), Classification::OptionalEnum}; - } - return TypeInfo{typeid(T), Classification::OptionalOther}; - } + template const static TypeInfo construct(std::optional fieldType); // No general case, only specialisations + template const static TypeInfo construct(std::optional fieldType = std::nullopt) { return TypeInfo{typeid(T), Classification::RequiredEnum , fieldType}; } + template const static TypeInfo construct(std::optional fieldType = std::nullopt) { return TypeInfo{typeid(T), Classification::RequiredOther, fieldType}; } + template const static TypeInfo construct(std::optional fieldType = std::nullopt) { return TypeInfo{typeid(T), Classification::OptionalEnum , fieldType}; } + template const static TypeInfo construct(std::optional fieldType = std::nullopt) { return TypeInfo{typeid(T), Classification::OptionalOther, fieldType}; } + }; + /** * \class TypeLookup allows us to get \c TypeInfo for a property. * @@ -177,11 +206,20 @@ class TypeLookup { * to. * * For the purposes of calling the \c TypeLookup constructor, the caller doesn't have to worry about what we - * are storing or how. For each property, you just provide the name of the property and the member variable in - * which it is stored, eg: - * PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::alpha_pct, Hop::m_alpha_pct) + * are storing or how. For each property, you just provide the name of the property, the member variable in + * which it is stored, and, if appropriate, the BtFieldType for the property eg: + * PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::notes , Hop::m_notes ), + * PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::alpha_pct, Hop::m_alpha_pct, NonPhysicalQuantity::Percentage), + * PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::amount_kg, Hop::m_amount_kg, Measurment::Mass ), * The macro and the templates above etc then do the necessary. + * + * Note that the introduction of __VA_OPT__ in C++20 makes dealing with the optional third argument a LOT less + * painful than it would otherwise be! + */ +#define PROPERTY_TYPE_LOOKUP_ENTRY(propNameConstVar, memberVar, ...) {&propNameConstVar, TypeInfo::construct(__VA_OPT__ (__VA_ARGS__))} + +/** + * \brief */ -#define PROPERTY_TYPE_LOOKUP_ENTRY(propNameConstVar, memberVar) {&propNameConstVar, TypeInfo::construct()} #endif diff --git a/src/widgets/SmartLineEdit.cpp b/src/widgets/SmartLineEdit.cpp index 8430f85e..7343fe84 100644 --- a/src/widgets/SmartLineEdit.cpp +++ b/src/widgets/SmartLineEdit.cpp @@ -44,7 +44,6 @@ class SmartLineEdit::impl { impl(SmartLineEdit & self) : m_self {self}, m_initialised {false}, - m_fieldType {NonPhysicalQuantity::String}, m_typeInfo {nullptr}, m_buddyLabel {nullptr}, m_uiAmountWithUnits {nullptr}, @@ -109,15 +108,13 @@ class SmartLineEdit::impl { * \brief We want to have two different signatures of \c SmartLineEdit::init so we can catch missing parameters at * compile time. Ultimately they both do pretty much the same work, by calling this function. */ - void init(BtFieldType const fieldType, - TypeInfo const & typeInfo, + void init(TypeInfo const & typeInfo, SmartLabel * buddyLabel, int const defaultPrecision, QString const & maximalDisplayString) { // It's a coding error to call this function twice on the same object, ie we should only initialise something once! Q_ASSERT(!this->m_initialised); - this->m_fieldType = fieldType; this->m_typeInfo = &typeInfo; this->m_buddyLabel = buddyLabel; this->m_defaultPrecision = defaultPrecision; @@ -125,7 +122,7 @@ class SmartLineEdit::impl { this->m_initialised = true; connect(&this->m_self, &QLineEdit::editingFinished, &this->m_self, &SmartLineEdit::onLineChanged); - if (!std::holds_alternative(this->m_fieldType)) { + if (!std::holds_alternative(*this->m_typeInfo->fieldType)) { QString configSection = this->m_self.property(*PropertyNames::UiAmountWithUnits::configSection).toString(); qDebug() << Q_FUNC_INFO << "Config Section =" << configSection; @@ -152,7 +149,6 @@ class SmartLineEdit::impl { SmartLineEdit & m_self; bool m_initialised; - BtFieldType m_fieldType; TypeInfo const * m_typeInfo; SmartLabel * m_buddyLabel; std::unique_ptr m_uiAmountWithUnits; @@ -172,39 +168,39 @@ SmartLineEdit::SmartLineEdit(QWidget * parent) : QLineEdit(parent), pimpl{std::m SmartLineEdit::~SmartLineEdit() = default; -void SmartLineEdit::init(Measurement::PhysicalQuantities const physicalQuantities, - TypeInfo const & typeInfo, +void SmartLineEdit::init(TypeInfo const & typeInfo, SmartLabel & buddyLabel, int const defaultPrecision, QString const & maximalDisplayString) { + // It's a coding error to call this version of init with a NonPhysicalQuantity + Q_ASSERT(typeInfo.fieldType && !std::holds_alternative(*typeInfo.fieldType)); + // It's only meaningful to have a UiAmountWithUnits if we are dealing with a PhysicalQuantity, hence why we do it // here and not in SmartLineEdit::impl::init(). // It's a coding error if we already created a UiAmountWithUnits Q_ASSERT(!this->pimpl->m_uiAmountWithUnits); - this->pimpl->m_uiAmountWithUnits = std::make_unique(this->parentWidget(), physicalQuantities); + this->pimpl->m_uiAmountWithUnits = std::make_unique(this->parentWidget(), ConvertToPhysicalQuantities(*typeInfo.fieldType)); // See above for why we need to pass these in to UiAmountWithUnits if (!this->pimpl->m_editField .isEmpty()) { this->pimpl->m_uiAmountWithUnits->setEditField (this->pimpl->m_editField ); } if (!this->pimpl->m_configSection.isEmpty()) { this->pimpl->m_uiAmountWithUnits->setConfigSection(this->pimpl->m_configSection); } - this->pimpl->init(ConvertToBtFieldType(physicalQuantities), - typeInfo, - &buddyLabel, - defaultPrecision, - maximalDisplayString); + this->pimpl->init(typeInfo, &buddyLabel, defaultPrecision, maximalDisplayString); return; } -void SmartLineEdit::init(NonPhysicalQuantity const nonPhysicalQuantity, - TypeInfo const & typeInfo, +void SmartLineEdit::init(TypeInfo const & typeInfo, int const defaultPrecision, QString const & maximalDisplayString) { - this->pimpl->init(nonPhysicalQuantity, typeInfo, nullptr, defaultPrecision, maximalDisplayString); + // It's a coding error to call this version of init with anything other than a NonPhysicalQuantity + Q_ASSERT(typeInfo.fieldType && std::holds_alternative(*typeInfo.fieldType)); + + this->pimpl->init(typeInfo, nullptr, defaultPrecision, maximalDisplayString); return; } BtFieldType const SmartLineEdit::getFieldType() const { Q_ASSERT(this->pimpl->m_initialised); - return this->pimpl->m_fieldType; + return *this->pimpl->m_typeInfo->fieldType; } TypeInfo const & SmartLineEdit::getTypeInfo() const { @@ -230,10 +226,10 @@ void SmartLineEdit::setAmount(std::optional amount, std::optional p if (!amount) { // What the field is measuring doesn't matter as it's not set this->QLineEdit::setText(""); - } else if (std::holds_alternative(this->pimpl->m_fieldType)) { + } else if (std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)) { // The field is not measuring a physical quantity so there are no units or unit conversions to handle - NonPhysicalQuantity const nonPhysicalQuantity = std::get(this->pimpl->m_fieldType); + NonPhysicalQuantity const nonPhysicalQuantity = std::get(*this->pimpl->m_typeInfo->fieldType); // It's a coding error if we're trying to pass a number in to a string field Q_ASSERT(nonPhysicalQuantity != NonPhysicalQuantity::String); @@ -276,7 +272,7 @@ QString SmartLineEdit::getForcedRelativeScaleViaString() const { Q_ASSERT( void SmartLineEdit::onLineChanged() { Q_ASSERT(this->pimpl->m_initialised); - if (std::holds_alternative(this->pimpl->m_fieldType)) { + if (std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)) { // The field is not measuring a physical quantity so there are no units or unit conversions to handle qDebug() << Q_FUNC_INFO; if (sender() == this) { @@ -288,11 +284,11 @@ void SmartLineEdit::onLineChanged() { // The field is measuring a physical quantity Q_ASSERT(this->pimpl->m_uiAmountWithUnits); qDebug() << - Q_FUNC_INFO << "this->fieldType=" << this->pimpl->m_fieldType << ", forcedSystemOfMeasurement=" << + Q_FUNC_INFO << "Field Type:" << *this->pimpl->m_typeInfo->fieldType << ", forcedSystemOfMeasurement=" << this->pimpl->m_uiAmountWithUnits->getForcedSystemOfMeasurement() << ", forcedRelativeScale=" << this->pimpl->m_uiAmountWithUnits->getForcedRelativeScale() << ", value=" << this->text(); - Measurement::PhysicalQuantities const physicalQuantities = ConvertToPhysicalQuantities(this->pimpl->m_fieldType); + Measurement::PhysicalQuantities const physicalQuantities = ConvertToPhysicalQuantities(*this->pimpl->m_typeInfo->fieldType); QString const propertyName = this->pimpl->m_uiAmountWithUnits->getEditField(); QString const configSection = this->pimpl->m_uiAmountWithUnits->getConfigSection(); diff --git a/src/widgets/SmartLineEdit.h b/src/widgets/SmartLineEdit.h index 4152b7c9..48e0a3b1 100644 --- a/src/widgets/SmartLineEdit.h +++ b/src/widgets/SmartLineEdit.h @@ -87,7 +87,8 @@ class SmartLineEdit : public QLineEdit { * above), it also ensures, if necessary, that the \c changedSystemOfMeasurementOrScale signal from the * \c SmartLabel buddy is connected to the \c lineChanged slot of this \c SmartLineEdit. * - * \param physicalQuantities Tells us what \c PhysicalQuantity (or \c Mixed2PhysicalQuantities) this field holds + * This version is for a \c PhysicalQuantity (or \c Mixed2PhysicalQuantities) field + * * \param typeInfo Tells us what data type we use to store the contents of the field (when converted to * canonical units if it is a \c PhysicalQuantity) and, whether this is an optional * field (in which case we need to handle blank / empty string as a valid value). @@ -95,20 +96,18 @@ class SmartLineEdit : public QLineEdit { * \param defaultPrecision Where relevant determines the number of decimal places to show * \param maximalDisplayString Used for determining the width of the widget */ - void init(Measurement::PhysicalQuantities const physicalQuantities, - TypeInfo const & typeInfo, - SmartLabel & buddyLabel, - int const defaultPrecision = 3, - QString const & maximalDisplayString = "100.000 srm"); + void init(TypeInfo const & typeInfo, + SmartLabel & buddyLabel, + int const defaultPrecision = 3, + QString const & maximalDisplayString = "100.000 srm"); /** * \brief As above, but for non-physical quantity such as \c NonPhysicalQuantity::Date, * \c NonPhysicalQuantity::String, etc. */ - void init(NonPhysicalQuantity const nonPhysicalQuantity, - TypeInfo const & typeInfo, - int const defaultPrecision = 3, - QString const & maximalDisplayString = "100.000 srm"); + void init(TypeInfo const & typeInfo, + int const defaultPrecision = 3, + QString const & maximalDisplayString = "100.000 srm"); BtFieldType const getFieldType() const; From bcde371022dd10f01dea45a9db4954f84ca75bb2 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Wed, 29 Mar 2023 11:25:17 +0200 Subject: [PATCH 05/42] Build fixes and start on using SmartLineEdit more widely --- CMakeLists.txt | 11 +++++-- meson.build | 4 +++ src/BrewNoteWidget.cpp | 12 +++++++ src/model/BrewNote.cpp | 48 ++++++++++++++-------------- src/model/BrewNote.h | 1 + src/model/Equipment.h | 1 + src/model/Fermentable.h | 1 + src/model/Hop.h | 1 + src/model/Instruction.h | 1 + src/model/Inventory.h | 1 + src/model/Mash.h | 1 + src/model/MashStep.h | 1 + src/model/Misc.h | 1 + src/model/NamedEntity.h | 7 ++++ src/model/NamedEntityWithInventory.h | 1 + src/model/Recipe.h | 1 + src/model/Salt.h | 1 + src/model/Style.h | 1 + src/model/Water.h | 1 + src/model/Yeast.h | 1 + 20 files changed, 71 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index edde01b3..ad2be420 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -242,8 +242,8 @@ set(RUNTIME_INSTALL_COMPONENT "Runtime") # We use different compilers on different platforms. Where possible, we want to let CMake handle the actual compiler # settings set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -# We need C++20 for std::map::contains(), C++17 or later for nested namespaces and structured bindings, and C++11 or -# later for lambdas. +# We need C++20 for std::map::contains() and concepts, C++17 or later for nested namespaces and structured bindings, and +# C++11 or later for lambdas. set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) @@ -285,6 +285,13 @@ if(NOT ${NO_MESSING_WITH_FLAGS}) else() set(CMAKE_CXX_FLAGS_DEBUG "-Wall -g3 -no-pie -fno-pie") endif() + + # + # On older versions of GCC, it is not sufficient to specify C++20 to enable concepts, you also have to set a + # special compiler flag. + # + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fconcepts") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fconcepts") endif() # Speed up compilation if using gcc. diff --git a/meson.build b/meson.build index f877533f..9e89a88f 100644 --- a/meson.build +++ b/meson.build @@ -1417,6 +1417,9 @@ if compiler.get_id() == 'gcc' # However, even this is not sufficient(!). So, for the moment, we suppress the rpmlint error (see # packaging/rpmLintFilters.toml). # + # -fconcepts Is needed on older versions of GCC to enable C++20 concepts (because specifying C++20 was not enough + # for some reason). + # # The following are, according to some comments at # https://stackoverflow.com/questions/52583544/boost-stack-trace-not-showing-function-names-and-line-numbers, needed # for Boost stacktrace to work properly: @@ -1432,6 +1435,7 @@ if compiler.get_id() == 'gcc' add_global_arguments(['-g3', '-O2', '-z', 'noexecstack', # NB Not '-z noexecstack' as otherwise will be passed to gcc in quotes! + '-fconcepts' ], language : 'cpp') if host_machine.system() == 'windows' add_global_arguments (['-no-pie', '-fno-pie'], language : 'cpp') diff --git a/src/BrewNoteWidget.cpp b/src/BrewNoteWidget.cpp index 478f110c..cb9e244e 100644 --- a/src/BrewNoteWidget.cpp +++ b/src/BrewNoteWidget.cpp @@ -38,6 +38,18 @@ BrewNoteWidget::BrewNoteWidget(QWidget *parent) : QWidget(parent) { bNoteObs = 0; setObjectName("BrewNoteWidget"); +/// +///.. lineEdit_FG ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::fg), +///.. lineEdit_OG ->init(); x class="BtDensityEdit" name= +///.. lineEdit_SG ->init(); x class="BtDensityEdit" name= +///.. lineEdit_mashFinTemp->init(); x class="BtTemperatureEdit" name= +///.. lineEdit_pitchTemp ->init(); x class="BtTemperatureEdit" name= +///.. lineEdit_strikeTemp ->init(); x class="BtTemperatureEdit" name= +///.. lineEdit_finalVol ->init(); x class="BtVolumeEdit" name= +///.. lineEdit_postBoilVol->init(); x class="BtVolumeEdit" name= +///.. lineEdit_volIntoBK ->init(); x class="BtVolumeEdit" name= +///.. lineEdit_volIntoFerm->init(); x class="BtVolumeEdit" name= + connect(lineEdit_SG, &BtLineEdit::textModified, this, &BrewNoteWidget::updateSG); connect(lineEdit_volIntoBK, &BtLineEdit::textModified, this, &BrewNoteWidget::updateVolumeIntoBK_l); connect(lineEdit_strikeTemp, &BtLineEdit::textModified, this, &BrewNoteWidget::updateStrikeTemp_c); diff --git a/src/model/BrewNote.cpp b/src/model/BrewNote.cpp index 541a5b75..c07ebbbb 100644 --- a/src/model/BrewNote.cpp +++ b/src/model/BrewNote.cpp @@ -67,35 +67,35 @@ TypeLookup const BrewNote::typeLookup { // Note that we need Enums to be treated as ints for the purposes of type lookup PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::abv , BrewNote::m_abv ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::attenuation , BrewNote::m_attenuation ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::boilOff_l , BrewNote::m_boilOff_l ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::brewDate , BrewNote::m_brewDate ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::brewhouseEff_pct , BrewNote::m_brewhouseEff_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::effIntoBK_pct , BrewNote::m_effIntoBK_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::fermentDate , BrewNote::m_fermentDate ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::fg , BrewNote::m_fg ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::finalVolume_l , BrewNote::m_finalVolume_l ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::mashFinTemp_c , BrewNote::m_mashFinTemp_c ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::boilOff_l , BrewNote::m_boilOff_l , Measurement::PhysicalQuantity::Volume), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::brewDate , BrewNote::m_brewDate , NonPhysicalQuantity::Date), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::brewhouseEff_pct , BrewNote::m_brewhouseEff_pct , NonPhysicalQuantity::Percentage), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::effIntoBK_pct , BrewNote::m_effIntoBK_pct , NonPhysicalQuantity::Percentage), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::fermentDate , BrewNote::m_fermentDate , NonPhysicalQuantity::Date), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::fg , BrewNote::m_fg , Measurement::PhysicalQuantity::Density), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::finalVolume_l , BrewNote::m_finalVolume_l , Measurement::PhysicalQuantity::Volume), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::mashFinTemp_c , BrewNote::m_mashFinTemp_c , Measurement::PhysicalQuantity::Temperature), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::notes , BrewNote::m_notes ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::og , BrewNote::m_og ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::pitchTemp_c , BrewNote::m_pitchTemp_c ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::postBoilVolume_l , BrewNote::m_postBoilVolume_l ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projABV_pct , BrewNote::m_projABV_pct ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::og , BrewNote::m_og , Measurement::PhysicalQuantity::Density), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::pitchTemp_c , BrewNote::m_pitchTemp_c , Measurement::PhysicalQuantity::Temperature), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::postBoilVolume_l , BrewNote::m_postBoilVolume_l , Measurement::PhysicalQuantity::Volume), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projABV_pct , BrewNote::m_projABV_pct , NonPhysicalQuantity::Percentage), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projAtten , BrewNote::m_projAtten ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projBoilGrav , BrewNote::m_projBoilGrav ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projEff_pct , BrewNote::m_projEff_pct ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projBoilGrav , BrewNote::m_projBoilGrav , Measurement::PhysicalQuantity::Density), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projEff_pct , BrewNote::m_projEff_pct , NonPhysicalQuantity::Percentage), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projFermPoints , BrewNote::m_projFermPoints ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projFg , BrewNote::m_projFg ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projMashFinTemp_c, BrewNote::m_projMashFinTemp_c), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projOg , BrewNote::m_projOg ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projFg , BrewNote::m_projFg , Measurement::PhysicalQuantity::Density), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projMashFinTemp_c, BrewNote::m_projMashFinTemp_c, Measurement::PhysicalQuantity::Temperature), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projOg , BrewNote::m_projOg , Measurement::PhysicalQuantity::Density), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projPoints , BrewNote::m_projPoints ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projStrikeTemp_c , BrewNote::m_projStrikeTemp_c ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projVolIntoBK_l , BrewNote::m_projVolIntoBK_l ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projVolIntoFerm_l, BrewNote::m_projVolIntoFerm_l), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projStrikeTemp_c , BrewNote::m_projStrikeTemp_c , Measurement::PhysicalQuantity::Temperature), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projVolIntoBK_l , BrewNote::m_projVolIntoBK_l , Measurement::PhysicalQuantity::Volume), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projVolIntoFerm_l, BrewNote::m_projVolIntoFerm_l, Measurement::PhysicalQuantity::Volume), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::recipeId , BrewNote::m_recipeId ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::sg , BrewNote::m_sg ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::strikeTemp_c , BrewNote::m_strikeTemp_c ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::volumeIntoBK_l , BrewNote::m_volumeIntoBK_l ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::volumeIntoFerm_l , BrewNote::m_volumeIntoFerm_l ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::sg , BrewNote::m_sg , Measurement::PhysicalQuantity::Density), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::strikeTemp_c , BrewNote::m_strikeTemp_c , Measurement::PhysicalQuantity::Temperature), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::volumeIntoBK_l , BrewNote::m_volumeIntoBK_l , Measurement::PhysicalQuantity::Volume), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::volumeIntoFerm_l , BrewNote::m_volumeIntoFerm_l , Measurement::PhysicalQuantity::Volume), }, // Parent class lookup &NamedEntity::typeLookup diff --git a/src/model/BrewNote.h b/src/model/BrewNote.h index ffa291b5..a1f83857 100644 --- a/src/model/BrewNote.h +++ b/src/model/BrewNote.h @@ -33,6 +33,7 @@ //====================================================================================================================== //========================================== Start of property name constants ========================================== +// See comment in model/NamedEntity.h #define AddPropertyName(property) namespace PropertyNames::BrewNote { BtStringConst const property{#property}; } AddPropertyName(abv ) AddPropertyName(attenuation ) diff --git a/src/model/Equipment.h b/src/model/Equipment.h index 35a4bec4..f6346769 100644 --- a/src/model/Equipment.h +++ b/src/model/Equipment.h @@ -29,6 +29,7 @@ //====================================================================================================================== //========================================== Start of property name constants ========================================== +// See comment in model/NamedEntity.h #define AddPropertyName(property) namespace PropertyNames::Equipment { BtStringConst const property{#property}; } AddPropertyName(batchSize_l ) AddPropertyName(boilingPoint_c ) diff --git a/src/model/Fermentable.h b/src/model/Fermentable.h index f6f5d1c0..7095e827 100644 --- a/src/model/Fermentable.h +++ b/src/model/Fermentable.h @@ -41,6 +41,7 @@ //====================================================================================================================== //========================================== Start of property name constants ========================================== +// See comment in model/NamedEntity.h #define AddPropertyName(property) namespace PropertyNames::Fermentable { BtStringConst const property{#property}; } AddPropertyName(addAfterBoil ) AddPropertyName(alphaAmylase_dextUnits) diff --git a/src/model/Hop.h b/src/model/Hop.h index 053c1150..bdede5f2 100644 --- a/src/model/Hop.h +++ b/src/model/Hop.h @@ -34,6 +34,7 @@ //====================================================================================================================== //========================================== Start of property name constants ========================================== +// See comment in model/NamedEntity.h #define AddPropertyName(property) namespace PropertyNames::Hop { BtStringConst const property{#property}; } AddPropertyName(alpha_pct ) AddPropertyName(amount_kg ) diff --git a/src/model/Instruction.h b/src/model/Instruction.h index 2edabc04..4cad38ec 100644 --- a/src/model/Instruction.h +++ b/src/model/Instruction.h @@ -31,6 +31,7 @@ //====================================================================================================================== //========================================== Start of property name constants ========================================== +// See comment in model/NamedEntity.h #define AddPropertyName(property) namespace PropertyNames::Instruction { BtStringConst const property{#property}; } AddPropertyName(completed ) AddPropertyName(directions) diff --git a/src/model/Inventory.h b/src/model/Inventory.h index 313460c6..527fdcf7 100644 --- a/src/model/Inventory.h +++ b/src/model/Inventory.h @@ -28,6 +28,7 @@ class TypeLookup; //====================================================================================================================== //========================================== Start of property name constants ========================================== +// See comment in model/NamedEntity.h #define AddPropertyName(property) namespace PropertyNames::Inventory { BtStringConst const property{#property}; } AddPropertyName(id) AddPropertyName(amount) diff --git a/src/model/Mash.h b/src/model/Mash.h index 96714e36..c8dcfa02 100644 --- a/src/model/Mash.h +++ b/src/model/Mash.h @@ -36,6 +36,7 @@ //====================================================================================================================== //========================================== Start of property name constants ========================================== +// See comment in model/NamedEntity.h #define AddPropertyName(property) namespace PropertyNames::Mash { BtStringConst const property{#property}; } AddPropertyName(equipAdjust ) AddPropertyName(grainTemp_c ) diff --git a/src/model/MashStep.h b/src/model/MashStep.h index 56ab2fb9..bfef1734 100644 --- a/src/model/MashStep.h +++ b/src/model/MashStep.h @@ -30,6 +30,7 @@ //====================================================================================================================== //========================================== Start of property name constants ========================================== +// See comment in model/NamedEntity.h #define AddPropertyName(property) namespace PropertyNames::MashStep { BtStringConst const property{#property}; } AddPropertyName(decoctionAmount_l) AddPropertyName(endTemp_c ) diff --git a/src/model/Misc.h b/src/model/Misc.h index 74cb8d66..468664cf 100644 --- a/src/model/Misc.h +++ b/src/model/Misc.h @@ -31,6 +31,7 @@ //====================================================================================================================== //========================================== Start of property name constants ========================================== +// See comment in model/NamedEntity.h #define AddPropertyName(property) namespace PropertyNames::Misc { BtStringConst const property{#property}; } AddPropertyName(amount ) AddPropertyName(amountIsWeight) diff --git a/src/model/NamedEntity.h b/src/model/NamedEntity.h index 2c1b9408..df1c03d5 100644 --- a/src/model/NamedEntity.h +++ b/src/model/NamedEntity.h @@ -49,6 +49,13 @@ class Recipe; // every platform there is always exactly one instance of each property name. So, whilst it's always valid to compare // the values of two property names, we cannot _guarantee_ that two identical property names always have the same // address in memory. In other words, _don't_ do `if (&somePropName == &PropertyNames::NamedEntity::Folder) ...`. +// +// I did also think about creating a macro that would combine this with Q_PROPERTY, but I didn't see an elegant way to +// do it given that these need to be outside the class and Q_PROPERTY needs to be inside it. +// +// IMPORTANT: These property names are unique within a class, but they are not globally unique, so we have to be a bit +// careful about how we use them in look-ups. +// #define AddPropertyName(property) namespace PropertyNames::NamedEntity { BtStringConst const property{#property}; } AddPropertyName(deleted) AddPropertyName(display) diff --git a/src/model/NamedEntityWithInventory.h b/src/model/NamedEntityWithInventory.h index dc3e22eb..73989b99 100644 --- a/src/model/NamedEntityWithInventory.h +++ b/src/model/NamedEntityWithInventory.h @@ -21,6 +21,7 @@ //====================================================================================================================== //========================================== Start of property name constants ========================================== +// See comment in model/NamedEntity.h #define AddPropertyName(property) namespace PropertyNames::NamedEntityWithInventory { BtStringConst const property{#property}; } AddPropertyName(inventory ) AddPropertyName(inventoryId) diff --git a/src/model/Recipe.h b/src/model/Recipe.h index 0bc23759..bb0f5b0f 100644 --- a/src/model/Recipe.h +++ b/src/model/Recipe.h @@ -44,6 +44,7 @@ //====================================================================================================================== //========================================== Start of property name constants ========================================== +// See comment in model/NamedEntity.h #define AddPropertyName(property) namespace PropertyNames::Recipe { BtStringConst const property{#property}; } AddPropertyName(ABV_pct ) AddPropertyName(age_days ) diff --git a/src/model/Salt.h b/src/model/Salt.h index 9f750268..35015a89 100644 --- a/src/model/Salt.h +++ b/src/model/Salt.h @@ -29,6 +29,7 @@ //====================================================================================================================== //========================================== Start of property name constants ========================================== +// See comment in model/NamedEntity.h #define AddPropertyName(property) namespace PropertyNames::Salt { BtStringConst const property{#property}; } AddPropertyName(amount ) AddPropertyName(amountIsWeight) diff --git a/src/model/Style.h b/src/model/Style.h index b8b95231..ed752711 100644 --- a/src/model/Style.h +++ b/src/model/Style.h @@ -30,6 +30,7 @@ //====================================================================================================================== //========================================== Start of property name constants ========================================== +// See comment in model/NamedEntity.h #define AddPropertyName(property) namespace PropertyNames::Style { BtStringConst const property{#property}; } AddPropertyName(abvMax_pct ) AddPropertyName(abvMin_pct ) diff --git a/src/model/Water.h b/src/model/Water.h index 54319c27..27ea1641 100644 --- a/src/model/Water.h +++ b/src/model/Water.h @@ -29,6 +29,7 @@ //====================================================================================================================== //========================================== Start of property name constants ========================================== +// See comment in model/NamedEntity.h #define AddPropertyName(property) namespace PropertyNames::Water { BtStringConst const property{#property}; } AddPropertyName(alkalinity ) AddPropertyName(alkalinityAsHCO3) diff --git a/src/model/Yeast.h b/src/model/Yeast.h index 9f9c3636..bc76afd4 100644 --- a/src/model/Yeast.h +++ b/src/model/Yeast.h @@ -31,6 +31,7 @@ //====================================================================================================================== //========================================== Start of property name constants ========================================== +// See comment in model/NamedEntity.h #define AddPropertyName(property) namespace PropertyNames::Yeast { BtStringConst const property{#property}; } AddPropertyName(addToSecondary ) AddPropertyName(amount ) From 57b8e551fa2c0d4bafa20ab3e5b8eab2d23405e0 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Wed, 29 Mar 2023 13:25:48 +0200 Subject: [PATCH 06/42] Compilation fixes for old GCC --- src/utils/TypeLookup.h | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/utils/TypeLookup.h b/src/utils/TypeLookup.h index 889015df..f97d934c 100644 --- a/src/utils/TypeLookup.h +++ b/src/utils/TypeLookup.h @@ -56,25 +56,27 @@ * \c PROPERTY_TYPE_LOOKUP_ENTRY macro below takes care of everything for constructor calls where you would * otherwise need them. */ -template -struct is_optional : public std::false_type{}; -template -struct is_optional< std::optional > : public std::true_type{}; +template struct is_optional : public std::false_type{}; +template struct is_optional< std::optional > : public std::true_type{}; -template -struct is_optional_enum : public std::false_type{}; -template -struct is_optional_enum< std::optional > : public std::is_enum{}; +template struct is_optional_enum : public std::false_type{}; +template struct is_optional_enum< std::optional > : public std::is_enum{}; // This bit requires C++20 or later. It makes the specialisations of TypeInfo::construct() below a bit less clunky -template -concept IsRequiredEnum = std::is_enum::value; -template -concept IsRequiredOther = !std::is_enum::value && !is_optional::value; -template -concept IsOptionalEnum = is_optional_enum::value; -template -concept IsOptionalOther = !is_optional_enum::value && is_optional::value; +// Older versions of GCC (eg as shipped with Ubuntu 20.04 LTS) have a sort of pre-release support for concepts so we +// have to use non-standard syntax there + +#if defined(__GNUC__) && (__GNUC__ < 10) +template concept bool IsRequiredEnum = std::is_enum::value; +template concept bool IsRequiredOther = !std::is_enum::value && !is_optional::value; +template concept bool IsOptionalEnum = is_optional_enum::value; +template concept bool IsOptionalOther = !is_optional_enum::value && is_optional::value; +#else +template concept IsRequiredEnum = std::is_enum::value; +template concept IsRequiredOther = !std::is_enum::value && !is_optional::value; +template concept IsOptionalEnum = is_optional_enum::value; +template concept IsOptionalOther = !is_optional_enum::value && is_optional::value; +#endif /** * \brief Extends \c std::type_index with some other info we need about a type for serialisation, specifically whether From 415cadaf6ee0f3669bf38abf5c14b2fcde6ded23 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Wed, 29 Mar 2023 13:53:12 +0200 Subject: [PATCH 07/42] Compilation fixes 2 for old GCC --- .github/workflows/linux-ubuntu.yml | 52 +++++++++++++++--------------- CMakeLists.txt | 3 +- translations/bt_ca.ts | 29 ----------------- translations/bt_cs.ts | 29 ----------------- translations/bt_de.ts | 29 ----------------- translations/bt_el.ts | 29 ----------------- translations/bt_en.ts | 29 ----------------- translations/bt_es.ts | 29 ----------------- translations/bt_et.ts | 29 ----------------- translations/bt_eu.ts | 29 ----------------- translations/bt_fr.ts | 29 ----------------- translations/bt_gl.ts | 29 ----------------- translations/bt_hu.ts | 29 ----------------- translations/bt_it.ts | 29 ----------------- translations/bt_lv.ts | 29 ----------------- translations/bt_nb.ts | 29 ----------------- translations/bt_nl.ts | 18 ++--------- translations/bt_pl.ts | 29 ----------------- translations/bt_pt.ts | 29 ----------------- translations/bt_ru.ts | 29 ----------------- translations/bt_sr.ts | 29 ----------------- translations/bt_sv.ts | 29 ----------------- translations/bt_tr.ts | 29 ----------------- translations/bt_zh.ts | 29 ----------------- 24 files changed, 30 insertions(+), 652 deletions(-) diff --git a/.github/workflows/linux-ubuntu.yml b/.github/workflows/linux-ubuntu.yml index 8ee7c9ba..4cacbe28 100644 --- a/.github/workflows/linux-ubuntu.yml +++ b/.github/workflows/linux-ubuntu.yml @@ -73,6 +73,31 @@ jobs: pwd ./bt -v setup all + - name: Build (with Meson) + working-directory: ${{github.workspace}}/mbuild + shell: bash + run: | + pwd + meson compile + + # The 'export QT_DEBUG_PLUGINS=1' give us diagnostics in the event that there are problems initialising QT + # The 'export QT_QPA_PLATFORM=offscreen' stops Qt's xcb sub-module trying to connect to a non-existent display + # (which would cause the test runner to abort before running any tests). + - name: Test (via Meson) + working-directory: ${{github.workspace}}/mbuild + shell: bash + run: | + export QT_DEBUG_PLUGINS=1 + export QT_QPA_PLATFORM=offscreen + meson test + + - name: Package (New) + working-directory: ${{github.workspace}}/mbuild + shell: bash + run: | + umask 022 + ../bt package + - name: Create CMake build environment run: cmake -E make_directory ${{github.workspace}}/build @@ -95,13 +120,6 @@ jobs: find ../third-party make - - name: Build (with Meson) - working-directory: ${{github.workspace}}/mbuild - shell: bash - run: | - pwd - meson compile - - name: Test (via CMake) working-directory: ${{github.workspace}}/build shell: bash @@ -111,17 +129,6 @@ jobs: run: | make test - # The 'export QT_DEBUG_PLUGINS=1' give us diagnostics in the event that there are problems initialising QT - # The 'export QT_QPA_PLATFORM=offscreen' stops Qt's xcb sub-module trying to connect to a non-existent display - # (which would cause the test runner to abort before running any tests). - - name: Test (via Meson) - working-directory: ${{github.workspace}}/mbuild - shell: bash - run: | - export QT_DEBUG_PLUGINS=1 - export QT_QPA_PLATFORM=offscreen - meson test - - name: Package (via CMake/CPack) working-directory: ${{github.workspace}}/build shell: bash @@ -129,14 +136,7 @@ jobs: umask 022 make package - - name: Package (New) - working-directory: ${{github.workspace}}/mbuild - shell: bash - run: | - umask 022 - ../bt package - - - name: LintianAndRpmLint + - name: LintianAndRpmLint (for CMake/CPack) continue-on-error: true working-directory: ${{github.workspace}}/build shell: bash diff --git a/CMakeLists.txt b/CMakeLists.txt index ad2be420..9dff5e8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -290,8 +290,7 @@ if(NOT ${NO_MESSING_WITH_FLAGS}) # On older versions of GCC, it is not sufficient to specify C++20 to enable concepts, you also have to set a # special compiler flag. # - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fconcepts") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fconcepts") + add_compile_options(-fconcepts) endif() # Speed up compilation if using gcc. diff --git a/translations/bt_ca.ts b/translations/bt_ca.ts index 6c07cfd3..b73f5a55 100644 --- a/translations/bt_ca.ts +++ b/translations/bt_ca.ts @@ -2763,35 +2763,6 @@ Log file may contain more details.
- - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_cs.ts b/translations/bt_cs.ts index 59980245..16bbcecf 100644 --- a/translations/bt_cs.ts +++ b/translations/bt_cs.ts @@ -2695,35 +2695,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_de.ts b/translations/bt_de.ts index 28667911..202d8855 100644 --- a/translations/bt_de.ts +++ b/translations/bt_de.ts @@ -2731,35 +2731,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_el.ts b/translations/bt_el.ts index 1aed55b8..4653005b 100644 --- a/translations/bt_el.ts +++ b/translations/bt_el.ts @@ -2699,35 +2699,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_en.ts b/translations/bt_en.ts index b040d70a..f934b8ce 100644 --- a/translations/bt_en.ts +++ b/translations/bt_en.ts @@ -2203,35 +2203,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_es.ts b/translations/bt_es.ts index 33b2ff4f..6c770629 100644 --- a/translations/bt_es.ts +++ b/translations/bt_es.ts @@ -2751,35 +2751,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_et.ts b/translations/bt_et.ts index f9594b17..a861e1c7 100644 --- a/translations/bt_et.ts +++ b/translations/bt_et.ts @@ -2254,35 +2254,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_eu.ts b/translations/bt_eu.ts index 10f6ed9c..994efcdd 100644 --- a/translations/bt_eu.ts +++ b/translations/bt_eu.ts @@ -2262,35 +2262,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_fr.ts b/translations/bt_fr.ts index bf59208a..deb4f44d 100644 --- a/translations/bt_fr.ts +++ b/translations/bt_fr.ts @@ -2767,35 +2767,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_gl.ts b/translations/bt_gl.ts index 884ea60c..c7723d56 100644 --- a/translations/bt_gl.ts +++ b/translations/bt_gl.ts @@ -2377,35 +2377,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_hu.ts b/translations/bt_hu.ts index b5703f1a..90865c0a 100644 --- a/translations/bt_hu.ts +++ b/translations/bt_hu.ts @@ -2739,35 +2739,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_it.ts b/translations/bt_it.ts index f70aa33c..5a234e8d 100644 --- a/translations/bt_it.ts +++ b/translations/bt_it.ts @@ -2767,35 +2767,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_lv.ts b/translations/bt_lv.ts index 5290126e..6bd6f6c9 100644 --- a/translations/bt_lv.ts +++ b/translations/bt_lv.ts @@ -2317,35 +2317,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_nb.ts b/translations/bt_nb.ts index 71cc765f..d682dee3 100644 --- a/translations/bt_nb.ts +++ b/translations/bt_nb.ts @@ -2695,35 +2695,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_nl.ts b/translations/bt_nl.ts index 27887c26..20c15d24 100644 --- a/translations/bt_nl.ts +++ b/translations/bt_nl.ts @@ -2783,31 +2783,19 @@ Log file may contain more details. QApplication Application terminates - De applicatie stopt + De applicatie stopt The application encountered a fatal error. Error message: %1 - The applicatie kreeg een fatale fout. + The applicatie kreeg een fatale fout. Foutmelding: %1 The application encountered a fatal error. - The applicatie kreeg een fatale fout. - - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - + The applicatie kreeg een fatale fout. diff --git a/translations/bt_pl.ts b/translations/bt_pl.ts index 9d20237e..d0895ffb 100644 --- a/translations/bt_pl.ts +++ b/translations/bt_pl.ts @@ -2695,35 +2695,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_pt.ts b/translations/bt_pt.ts index 01ae4ad0..adc9387a 100644 --- a/translations/bt_pt.ts +++ b/translations/bt_pt.ts @@ -2715,35 +2715,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_ru.ts b/translations/bt_ru.ts index 203bcb31..ec7c6b8c 100644 --- a/translations/bt_ru.ts +++ b/translations/bt_ru.ts @@ -2743,35 +2743,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_sr.ts b/translations/bt_sr.ts index a5a5aadc..fc580d3d 100644 --- a/translations/bt_sr.ts +++ b/translations/bt_sr.ts @@ -2591,35 +2591,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_sv.ts b/translations/bt_sv.ts index 6114abcc..863c01db 100644 --- a/translations/bt_sv.ts +++ b/translations/bt_sv.ts @@ -2771,35 +2771,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_tr.ts b/translations/bt_tr.ts index 173e2a22..62643466 100644 --- a/translations/bt_tr.ts +++ b/translations/bt_tr.ts @@ -2258,35 +2258,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject diff --git a/translations/bt_zh.ts b/translations/bt_zh.ts index 14d4b0b3..1a8a904d 100644 --- a/translations/bt_zh.ts +++ b/translations/bt_zh.ts @@ -2663,35 +2663,6 @@ Log file may contain more details. - - QApplication - - Brewken is already running! - - - - Another instance of Brewken is already running. - -Running two copies of the program at once may lead to data loss. - -Press OK to quit. - - - - Application terminates - - - - The application encountered a fatal error. -Error message: -%1 - - - - The application encountered a fatal error. - - - QObject From c3c7611c9615636a8e08d67a45f9d7ed355a9797 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Wed, 29 Mar 2023 15:28:38 +0200 Subject: [PATCH 08/42] Compilation fixes 3 for old GCC --- .github/workflows/linux-ubuntu.yml | 52 +++++++++++----------- src/measurement/Amount.cpp | 69 +----------------------------- src/model/NamedParameterBundle.h | 4 +- src/utils/TypeLookup.h | 3 +- 4 files changed, 32 insertions(+), 96 deletions(-) diff --git a/.github/workflows/linux-ubuntu.yml b/.github/workflows/linux-ubuntu.yml index 4cacbe28..4b6140fc 100644 --- a/.github/workflows/linux-ubuntu.yml +++ b/.github/workflows/linux-ubuntu.yml @@ -73,31 +73,6 @@ jobs: pwd ./bt -v setup all - - name: Build (with Meson) - working-directory: ${{github.workspace}}/mbuild - shell: bash - run: | - pwd - meson compile - - # The 'export QT_DEBUG_PLUGINS=1' give us diagnostics in the event that there are problems initialising QT - # The 'export QT_QPA_PLATFORM=offscreen' stops Qt's xcb sub-module trying to connect to a non-existent display - # (which would cause the test runner to abort before running any tests). - - name: Test (via Meson) - working-directory: ${{github.workspace}}/mbuild - shell: bash - run: | - export QT_DEBUG_PLUGINS=1 - export QT_QPA_PLATFORM=offscreen - meson test - - - name: Package (New) - working-directory: ${{github.workspace}}/mbuild - shell: bash - run: | - umask 022 - ../bt package - - name: Create CMake build environment run: cmake -E make_directory ${{github.workspace}}/build @@ -118,7 +93,7 @@ jobs: run: | pwd find ../third-party - make + make VERBOSE=1 - name: Test (via CMake) working-directory: ${{github.workspace}}/build @@ -142,6 +117,31 @@ jobs: shell: bash run: make package_lint + - name: Build (with Meson) + working-directory: ${{github.workspace}}/mbuild + shell: bash + run: | + pwd + meson compile + + # The 'export QT_DEBUG_PLUGINS=1' give us diagnostics in the event that there are problems initialising QT + # The 'export QT_QPA_PLATFORM=offscreen' stops Qt's xcb sub-module trying to connect to a non-existent display + # (which would cause the test runner to abort before running any tests). + - name: Test (via Meson) + working-directory: ${{github.workspace}}/mbuild + shell: bash + run: | + export QT_DEBUG_PLUGINS=1 + export QT_QPA_PLATFORM=offscreen + meson test + + - name: Package (New) + working-directory: ${{github.workspace}}/mbuild + shell: bash + run: | + umask 022 + ../bt package + - name: Upload Linux Packages (Installers) if: ${{ success() }} uses: actions/upload-artifact@v3 diff --git a/src/measurement/Amount.cpp b/src/measurement/Amount.cpp index ff10b60f..78ae9f53 100644 --- a/src/measurement/Amount.cpp +++ b/src/measurement/Amount.cpp @@ -24,7 +24,7 @@ namespace Measurement { Amount::Amount(double quantity, Unit const & unit) : m_quantity{quantity}, m_unit{&unit} { return; - }; + } //! Copy constructor Amount::Amount(Amount const & other) = default; @@ -46,71 +46,6 @@ namespace Measurement { } -//// // Default constructor - constructs an invalid Amount -//// MassOrVolumeAmt::MassOrVolumeAmt() : Measurement::Amount{-999.999, Measurement::Units::kilograms} { -//// return; -//// } -//// -//// // Regular constructor -//// MassOrVolumeAmt::MassOrVolumeAmt(double quantity, -//// Measurement::Unit const & unit) : -//// Measurement::Amount{quantity, unit} { -//// if (!this->wasConstructAssignOrMoveOK()) { -//// qCritical() << Q_FUNC_INFO << "Trying to construct MassOrVolumeAmt with " << this->m_unit->name; -//// Q_ASSERT(false); -//// } -//// return; -//// } -//// -//// // Copy constructor -//// MassOrVolumeAmt::MassOrVolumeAmt(Measurement::Amount const & other) : Measurement::Amount{other} { -//// if (!this->wasConstructAssignOrMoveOK()) { -//// qCritical() << Q_FUNC_INFO << "Trying to copy construct MassOrVolumeAmt with " << this->m_unit->name; -//// Q_ASSERT(false); -//// } -//// return; -//// } -//// -//// // Assignment operator -//// MassOrVolumeAmt & MassOrVolumeAmt::operator=(Measurement::Amount const & other) { -//// Measurement::Amount::operator=(other); -//// if (!this->wasConstructAssignOrMoveOK()) { -//// qCritical() << Q_FUNC_INFO << "Trying to assign to MassOrVolumeAmt with " << this->m_unit->name; -//// Q_ASSERT(false); -//// } -//// return *this; -//// } -//// -//// // Move constructor -//// MassOrVolumeAmt::MassOrVolumeAmt(Measurement::Amount && other) : Measurement::Amount{other} { -//// if (!this->wasConstructAssignOrMoveOK()) { -//// qCritical() << Q_FUNC_INFO << "Trying to move construct MassOrVolumeAmt with " << this->m_unit->name; -//// Q_ASSERT(false); -//// } -//// return; -//// } -//// -//// //! Move assignment. -//// MassOrVolumeAmt & MassOrVolumeAmt::operator=(Measurement::Amount && other) { -//// Measurement::Amount::operator=(other); -//// if (!this->wasConstructAssignOrMoveOK()) { -//// qCritical() << Q_FUNC_INFO << "Trying to move assign MassOrVolumeAmt with " << this->m_unit->name; -//// Q_ASSERT(false); -//// } -//// return *this; -//// } -//// -//// bool MassOrVolumeAmt::isMass() const { -//// return this->Measurement::Amount::m_unit->getPhysicalQuantity() == Measurement::PhysicalQuantity::Mass; -//// } -//// -//// -//// bool MassOrVolumeAmt::wasConstructAssignOrMoveOK() { -//// return (this->Measurement::Amount::m_unit->getPhysicalQuantity() == Measurement::PhysicalQuantity::Mass || -//// this->Measurement::Amount::m_unit->getPhysicalQuantity() == Measurement::PhysicalQuantity::Volume); -//// } -//// - bool operator<(Measurement::Amount const & lhs, Measurement::Amount const & rhs) { // Amounts in the same units are trivial to compare if (lhs.unit() == rhs.unit()) { @@ -148,5 +83,5 @@ S & operator<<(S & stream, Measurement::Amount const amount) { // (This is all just a trick to allow the template definition to be here in the .cpp file and not in the header, which // means, amongst other things, that we can reference member functions of Measurement::Unit.) // -template QDebug & operator<<(QDebug & stream, Measurement::Amount const amount); +template QDebug & operator<<(QDebug & stream, Measurement::Amount const amount); template QTextStream & operator<<(QTextStream & stream, Measurement::Amount const amount); diff --git a/src/model/NamedParameterBundle.h b/src/model/NamedParameterBundle.h index f58c79b3..f466b2a5 100644 --- a/src/model/NamedParameterBundle.h +++ b/src/model/NamedParameterBundle.h @@ -59,8 +59,8 @@ Q_DECLARE_METATYPE(std::optional) Q_DECLARE_METATYPE(std::optional) // Need these to be able to use MassOrVolumeAmt in Qt Properties system -Q_DECLARE_METATYPE(MassOrVolumeAmt); -Q_DECLARE_METATYPE(std::optional); +Q_DECLARE_METATYPE(MassOrVolumeAmt ) +Q_DECLARE_METATYPE(std::optional) /** * \brief This allows constructors to be called without a long list of positional parameters and, more importantly, for diff --git a/src/utils/TypeLookup.h b/src/utils/TypeLookup.h index f97d934c..ae68afc6 100644 --- a/src/utils/TypeLookup.h +++ b/src/utils/TypeLookup.h @@ -62,10 +62,11 @@ template struct is_optional< std::optional > : public std::true_ template struct is_optional_enum : public std::false_type{}; template struct is_optional_enum< std::optional > : public std::is_enum{}; +// // This bit requires C++20 or later. It makes the specialisations of TypeInfo::construct() below a bit less clunky // Older versions of GCC (eg as shipped with Ubuntu 20.04 LTS) have a sort of pre-release support for concepts so we // have to use non-standard syntax there - +// #if defined(__GNUC__) && (__GNUC__ < 10) template concept bool IsRequiredEnum = std::is_enum::value; template concept bool IsRequiredOther = !std::is_enum::value && !is_optional::value; From 45b5eacf4741bc30ea3364f5a8469d0858c81994 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Wed, 29 Mar 2023 16:13:30 +0200 Subject: [PATCH 09/42] Compilation fixes 4 for old GCC plus more SmartLineEdit etc --- CMakeLists.txt | 12 ++- src/BrewNoteWidget.cpp | 216 +++++++++++------------------------------ src/MiscEditor.cpp | 2 +- translations/bt_ca.ts | 29 ++++++ translations/bt_cs.ts | 29 ++++++ translations/bt_de.ts | 29 ++++++ translations/bt_el.ts | 29 ++++++ translations/bt_en.ts | 29 ++++++ translations/bt_es.ts | 29 ++++++ translations/bt_et.ts | 29 ++++++ translations/bt_eu.ts | 29 ++++++ translations/bt_fr.ts | 29 ++++++ translations/bt_gl.ts | 29 ++++++ translations/bt_hu.ts | 29 ++++++ translations/bt_it.ts | 29 ++++++ translations/bt_lv.ts | 29 ++++++ translations/bt_nb.ts | 29 ++++++ translations/bt_nl.ts | 18 +++- translations/bt_pl.ts | 29 ++++++ translations/bt_pt.ts | 29 ++++++ translations/bt_ru.ts | 29 ++++++ translations/bt_sr.ts | 29 ++++++ translations/bt_sv.ts | 29 ++++++ translations/bt_tr.ts | 29 ++++++ translations/bt_zh.ts | 29 ++++++ ui/brewNoteWidget.ui | 139 ++++++++++---------------- 26 files changed, 739 insertions(+), 257 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9dff5e8f..0908bca1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -286,11 +286,6 @@ if(NOT ${NO_MESSING_WITH_FLAGS}) set(CMAKE_CXX_FLAGS_DEBUG "-Wall -g3 -no-pie -fno-pie") endif() - # - # On older versions of GCC, it is not sufficient to specify C++20 to enable concepts, you also have to set a - # special compiler flag. - # - add_compile_options(-fconcepts) endif() # Speed up compilation if using gcc. @@ -299,6 +294,13 @@ if(NOT ${NO_MESSING_WITH_FLAGS}) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pipe") endif() endif() +if(CMAKE_COMPILER_IS_GNUCXX) + # + # On older versions of GCC, it is not sufficient to specify C++20 to enable concepts, you also have to set a + # special compiler flag. + # + add_compile_options(-fconcepts) +endif() # Windows-specific compilation settings if(WIN32) diff --git a/src/BrewNoteWidget.cpp b/src/BrewNoteWidget.cpp index cb9e244e..2dea52fc 100644 --- a/src/BrewNoteWidget.cpp +++ b/src/BrewNoteWidget.cpp @@ -38,41 +38,38 @@ BrewNoteWidget::BrewNoteWidget(QWidget *parent) : QWidget(parent) { bNoteObs = 0; setObjectName("BrewNoteWidget"); -/// -///.. lineEdit_FG ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::fg), -///.. lineEdit_OG ->init(); x class="BtDensityEdit" name= -///.. lineEdit_SG ->init(); x class="BtDensityEdit" name= -///.. lineEdit_mashFinTemp->init(); x class="BtTemperatureEdit" name= -///.. lineEdit_pitchTemp ->init(); x class="BtTemperatureEdit" name= -///.. lineEdit_strikeTemp ->init(); x class="BtTemperatureEdit" name= -///.. lineEdit_finalVol ->init(); x class="BtVolumeEdit" name= -///.. lineEdit_postBoilVol->init(); x class="BtVolumeEdit" name= -///.. lineEdit_volIntoBK ->init(); x class="BtVolumeEdit" name= -///.. lineEdit_volIntoFerm->init(); x class="BtVolumeEdit" name= - - connect(lineEdit_SG, &BtLineEdit::textModified, this, &BrewNoteWidget::updateSG); - connect(lineEdit_volIntoBK, &BtLineEdit::textModified, this, &BrewNoteWidget::updateVolumeIntoBK_l); - connect(lineEdit_strikeTemp, &BtLineEdit::textModified, this, &BrewNoteWidget::updateStrikeTemp_c); - connect(lineEdit_mashFinTemp, &BtLineEdit::textModified, this, &BrewNoteWidget::updateMashFinTemp_c); - - connect(lineEdit_OG, &BtLineEdit::textModified, this, &BrewNoteWidget::updateOG); - connect(lineEdit_postBoilVol, &BtLineEdit::textModified, this, &BrewNoteWidget::updatePostBoilVolume_l); - connect(lineEdit_volIntoFerm, &BtLineEdit::textModified, this, &BrewNoteWidget::updateVolumeIntoFerm_l); - connect(lineEdit_pitchTemp, &BtLineEdit::textModified, this, &BrewNoteWidget::updatePitchTemp_c); - - connect(lineEdit_FG, &BtLineEdit::textModified, this, &BrewNoteWidget::updateFG); - connect(lineEdit_finalVol, &BtLineEdit::textModified, this, &BrewNoteWidget::updateFinalVolume_l); - connect(lineEdit_fermentDate, &QDateTimeEdit::dateChanged, this, &BrewNoteWidget::updateFermentDate); - - connect(btTextEdit_brewNotes, &BtTextEdit::textModified, this, &BrewNoteWidget::updateNotes); + this->lineEdit_Fg ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::fg ), *this->label_Fg ); + this->lineEdit_Og ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::og ), *this->label_Og ); + this->lineEdit_Sg ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::sg ), *this->label_Sg ); + this->lineEdit_mashFinTemp->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::mashFinTemp_c ), *this->label_mashFinTemp); + this->lineEdit_pitchTemp ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::pitchTemp_c ), *this->label_pitchTemp ); + this->lineEdit_strikeTemp ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::strikeTemp_c ), *this->label_strikeTemp ); + this->lineEdit_finalVolume->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::finalVolume_l ), *this->label_finalVolume ); + this->lineEdit_postBoilVol->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::postBoilVolume_l), *this->label_postBoilVol); + this->lineEdit_volIntoBk ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::volumeIntoBK_l ), *this->label_volIntoBk ); + this->lineEdit_volIntoFerm->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::volumeIntoFerm_l), *this->label_volIntoFerm); + + connect(this->lineEdit_Sg, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateSG ); + connect(this->lineEdit_volIntoBk, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateVolumeIntoBK_l ); + connect(this->lineEdit_strikeTemp, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateStrikeTemp_c ); + connect(this->lineEdit_mashFinTemp, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateMashFinTemp_c ); + connect(this->lineEdit_Og, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateOG ); + connect(this->lineEdit_postBoilVol, &SmartLineEdit::textModified, this, &BrewNoteWidget::updatePostBoilVolume_l); + connect(this->lineEdit_volIntoFerm, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateVolumeIntoFerm_l); + connect(this->lineEdit_pitchTemp, &SmartLineEdit::textModified, this, &BrewNoteWidget::updatePitchTemp_c ); + connect(this->lineEdit_Fg, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateFG ); + connect(this->lineEdit_finalVolume, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateFinalVolume_l ); + connect(this->lineEdit_fermentDate, &QDateTimeEdit::dateChanged, this, &BrewNoteWidget::updateFermentDate ); + connect(this->btTextEdit_brewNotes, &BtTextEdit::textModified, this, &BrewNoteWidget::updateNotes ); // A few labels on this page need special handling, so I connect them here // instead of how we would normally do this. - connect(btLabel_projectedOg, &BtLabel::changedSystemOfMeasurementOrScale, this, &BrewNoteWidget::updateProjOg); + connect(this->label_projectedOg, &SmartLabel::changedSystemOfMeasurementOrScale, this, &BrewNoteWidget::updateProjOg); /// connect(btLabel_fermentDate, &BtLabel::changedSystemOfMeasurementOrScale, this, &BrewNoteWidget::updateDateFormat); // I think this might work updateDateFormat(); + return; } BrewNoteWidget::~BrewNoteWidget() = default; @@ -152,118 +149,18 @@ bool BrewNoteWidget::isBrewNote(BrewNote* note) { return this->bNoteObs == note; } -void BrewNoteWidget::updateSG() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setSg(lineEdit_SG->toCanonical().quantity()); - return; -} - -void BrewNoteWidget::updateVolumeIntoBK_l() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setVolumeIntoBK_l(lineEdit_volIntoBK->toCanonical().quantity()); - return; -} - -void BrewNoteWidget::updateStrikeTemp_c() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setStrikeTemp_c(lineEdit_strikeTemp->toCanonical().quantity()); - return; -} - -void BrewNoteWidget::updateMashFinTemp_c() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setMashFinTemp_c(lineEdit_mashFinTemp->toCanonical().quantity()); - return; -} - -void BrewNoteWidget::updateOG() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setOg(lineEdit_OG->toCanonical().quantity()); - return; -} - -void BrewNoteWidget::updatePostBoilVolume_l() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setPostBoilVolume_l(lineEdit_postBoilVol->toCanonical().quantity()); - this->showChanges(); - return; -} - -void BrewNoteWidget::updateVolumeIntoFerm_l() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setVolumeIntoFerm_l(lineEdit_volIntoFerm->toCanonical().quantity()); - this->showChanges(); - return; -} - -void BrewNoteWidget::updatePitchTemp_c() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setPitchTemp_c(lineEdit_pitchTemp->toCanonical().quantity()); - this->showChanges(); - return; -} - -void BrewNoteWidget::updateFG() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setFg(lineEdit_FG->toCanonical().quantity()); - this->showChanges(); - return; -} - -void BrewNoteWidget::updateFinalVolume_l() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setFinalVolume_l(lineEdit_finalVol->toCanonical().quantity()); -// this->showChanges(); - return; -} - -void BrewNoteWidget::updateFermentDate(QDate const & datetime) { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setFermentDate(datetime); - return; -} - -void BrewNoteWidget::updateNotes() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setNotes(btTextEdit_brewNotes->toPlainText() ); - return; -} +void BrewNoteWidget::updateSG() { if (this->bNoteObs) { this->bNoteObs->setSg (this->lineEdit_Sg ->toCanonical().quantity()); } return; } +void BrewNoteWidget::updateVolumeIntoBK_l() { if (this->bNoteObs) { this->bNoteObs->setVolumeIntoBK_l (this->lineEdit_volIntoBk ->toCanonical().quantity()); } return; } +void BrewNoteWidget::updateStrikeTemp_c() { if (this->bNoteObs) { this->bNoteObs->setStrikeTemp_c (this->lineEdit_strikeTemp ->toCanonical().quantity()); } return; } +void BrewNoteWidget::updateMashFinTemp_c() { if (this->bNoteObs) { this->bNoteObs->setMashFinTemp_c (this->lineEdit_mashFinTemp->toCanonical().quantity()); } return; } +void BrewNoteWidget::updateOG() { if (this->bNoteObs) { this->bNoteObs->setOg (this->lineEdit_Og ->toCanonical().quantity()); } return; } +void BrewNoteWidget::updatePostBoilVolume_l() { if (this->bNoteObs) { this->bNoteObs->setPostBoilVolume_l(this->lineEdit_postBoilVol->toCanonical().quantity()); this->showChanges(); } return; } +void BrewNoteWidget::updateVolumeIntoFerm_l() { if (this->bNoteObs) { this->bNoteObs->setVolumeIntoFerm_l(this->lineEdit_volIntoFerm->toCanonical().quantity()); this->showChanges(); } return; } +void BrewNoteWidget::updatePitchTemp_c() { if (this->bNoteObs) { this->bNoteObs->setPitchTemp_c (this->lineEdit_pitchTemp ->toCanonical().quantity()); this->showChanges(); } return; } +void BrewNoteWidget::updateFG() { if (this->bNoteObs) { this->bNoteObs->setFg (this->lineEdit_Fg ->toCanonical().quantity()); this->showChanges(); } return; } +void BrewNoteWidget::updateFinalVolume_l() { if (this->bNoteObs) { this->bNoteObs->setFinalVolume_l (this->lineEdit_finalVolume ->toCanonical().quantity()); } return; } +void BrewNoteWidget::updateFermentDate(QDate const & datetime) { if (this->bNoteObs) { this->bNoteObs->setFermentDate (datetime); } return; } +void BrewNoteWidget::updateNotes() { if (this->bNoteObs) { this->bNoteObs->setNotes (this->btTextEdit_brewNotes->toPlainText() ); } return; } void BrewNoteWidget::changed([[maybe_unused]] QMetaProperty prop, [[maybe_unused]] QVariant val) { @@ -276,35 +173,34 @@ void BrewNoteWidget::changed([[maybe_unused]] QMetaProperty prop, } void BrewNoteWidget::showChanges([[maybe_unused]] QString field) { - if (this->bNoteObs == nullptr) { + if (!this->bNoteObs) { return; } - lineEdit_SG->setText(bNoteObs); - lineEdit_volIntoBK->setText(bNoteObs); - lineEdit_strikeTemp->setText(bNoteObs); - lineEdit_mashFinTemp->setText(bNoteObs); - lineEdit_OG->setText(bNoteObs); - lineEdit_postBoilVol->setText(bNoteObs); - lineEdit_volIntoFerm->setText(bNoteObs); - lineEdit_pitchTemp->setText(bNoteObs); - lineEdit_FG->setText(bNoteObs); - lineEdit_finalVol->setText(bNoteObs); - - lineEdit_fermentDate->setDate(bNoteObs->fermentDate()); - btTextEdit_brewNotes->setPlainText(bNoteObs->notes()); + this->lineEdit_Sg ->setAmount (bNoteObs->sg ()); + this->lineEdit_volIntoBk ->setAmount (bNoteObs->volumeIntoBK_l ()); + this->lineEdit_strikeTemp ->setAmount (bNoteObs->strikeTemp_c ()); + this->lineEdit_mashFinTemp->setAmount (bNoteObs->mashFinTemp_c ()); + this->lineEdit_Og ->setAmount (bNoteObs->og ()); + this->lineEdit_postBoilVol->setAmount (bNoteObs->postBoilVolume_l()); + this->lineEdit_volIntoFerm->setAmount (bNoteObs->volumeIntoFerm_l()); + this->lineEdit_pitchTemp ->setAmount (bNoteObs->pitchTemp_c ()); + this->lineEdit_Fg ->setAmount (bNoteObs->fg ()); + this->lineEdit_finalVolume ->setAmount (bNoteObs->finalVolume_l ()); + this->lineEdit_fermentDate->setDate (bNoteObs->fermentDate ()); + this->btTextEdit_brewNotes->setPlainText(bNoteObs->notes ()); // Now with the calculated stuff - lcdnumber_effBK->display(bNoteObs->effIntoBK_pct(),2); + this->lcdnumber_effBK->display(bNoteObs->effIntoBK_pct(),2); // Need to think about these? Maybe use the bubbles? this->updateProjOg(); // this requires more work, but updateProj does it - lcdnumber_brewhouseEff->display(bNoteObs->brewhouseEff_pct(),2); - lcdnumber_projABV->display(bNoteObs->projABV_pct(),2); - lcdnumber_abv->display(bNoteObs->abv(),2); - lcdnumber_atten->display(bNoteObs->attenuation(),2); - lcdnumber_projAtten->display(bNoteObs->projAtten(),2); + this->lcdnumber_brewhouseEff->display(bNoteObs->brewhouseEff_pct(), 2); + this->lcdnumber_projABV ->display(bNoteObs->projABV_pct (), 2); + this->lcdnumber_abv ->display(bNoteObs->abv (), 2); + this->lcdnumber_atten ->display(bNoteObs->attenuation (), 2); + this->lcdnumber_projAtten ->display(bNoteObs->projAtten (), 2); return; } diff --git a/src/MiscEditor.cpp b/src/MiscEditor.cpp index 784b69b1..7cdea216 100644 --- a/src/MiscEditor.cpp +++ b/src/MiscEditor.cpp @@ -59,7 +59,7 @@ void MiscEditor::setMisc(Misc * m) { obsMisc = m; if (obsMisc) { - connect(obsMisc, SIGNAL(changed(QMetaProperty, QVariant)), this, SLOT(changed(QMetaProperty, QVariant))); + connect(obsMisc, &NamedEntity::changed, this, &MiscEditor::changed); showChanges(); } return; diff --git a/translations/bt_ca.ts b/translations/bt_ca.ts index b73f5a55..6c07cfd3 100644 --- a/translations/bt_ca.ts +++ b/translations/bt_ca.ts @@ -2763,6 +2763,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_cs.ts b/translations/bt_cs.ts index 16bbcecf..59980245 100644 --- a/translations/bt_cs.ts +++ b/translations/bt_cs.ts @@ -2695,6 +2695,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_de.ts b/translations/bt_de.ts index 202d8855..28667911 100644 --- a/translations/bt_de.ts +++ b/translations/bt_de.ts @@ -2731,6 +2731,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_el.ts b/translations/bt_el.ts index 4653005b..1aed55b8 100644 --- a/translations/bt_el.ts +++ b/translations/bt_el.ts @@ -2699,6 +2699,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_en.ts b/translations/bt_en.ts index f934b8ce..b040d70a 100644 --- a/translations/bt_en.ts +++ b/translations/bt_en.ts @@ -2203,6 +2203,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_es.ts b/translations/bt_es.ts index 6c770629..33b2ff4f 100644 --- a/translations/bt_es.ts +++ b/translations/bt_es.ts @@ -2751,6 +2751,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_et.ts b/translations/bt_et.ts index a861e1c7..f9594b17 100644 --- a/translations/bt_et.ts +++ b/translations/bt_et.ts @@ -2254,6 +2254,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_eu.ts b/translations/bt_eu.ts index 994efcdd..10f6ed9c 100644 --- a/translations/bt_eu.ts +++ b/translations/bt_eu.ts @@ -2262,6 +2262,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_fr.ts b/translations/bt_fr.ts index deb4f44d..bf59208a 100644 --- a/translations/bt_fr.ts +++ b/translations/bt_fr.ts @@ -2767,6 +2767,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_gl.ts b/translations/bt_gl.ts index c7723d56..884ea60c 100644 --- a/translations/bt_gl.ts +++ b/translations/bt_gl.ts @@ -2377,6 +2377,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_hu.ts b/translations/bt_hu.ts index 90865c0a..b5703f1a 100644 --- a/translations/bt_hu.ts +++ b/translations/bt_hu.ts @@ -2739,6 +2739,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_it.ts b/translations/bt_it.ts index 5a234e8d..f70aa33c 100644 --- a/translations/bt_it.ts +++ b/translations/bt_it.ts @@ -2767,6 +2767,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_lv.ts b/translations/bt_lv.ts index 6bd6f6c9..5290126e 100644 --- a/translations/bt_lv.ts +++ b/translations/bt_lv.ts @@ -2317,6 +2317,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_nb.ts b/translations/bt_nb.ts index d682dee3..71cc765f 100644 --- a/translations/bt_nb.ts +++ b/translations/bt_nb.ts @@ -2695,6 +2695,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_nl.ts b/translations/bt_nl.ts index 20c15d24..27887c26 100644 --- a/translations/bt_nl.ts +++ b/translations/bt_nl.ts @@ -2783,19 +2783,31 @@ Log file may contain more details. QApplication Application terminates - De applicatie stopt + De applicatie stopt The application encountered a fatal error. Error message: %1 - The applicatie kreeg een fatale fout. + The applicatie kreeg een fatale fout. Foutmelding: %1 The application encountered a fatal error. - The applicatie kreeg een fatale fout. + The applicatie kreeg een fatale fout. + + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + diff --git a/translations/bt_pl.ts b/translations/bt_pl.ts index d0895ffb..9d20237e 100644 --- a/translations/bt_pl.ts +++ b/translations/bt_pl.ts @@ -2695,6 +2695,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_pt.ts b/translations/bt_pt.ts index adc9387a..01ae4ad0 100644 --- a/translations/bt_pt.ts +++ b/translations/bt_pt.ts @@ -2715,6 +2715,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_ru.ts b/translations/bt_ru.ts index ec7c6b8c..203bcb31 100644 --- a/translations/bt_ru.ts +++ b/translations/bt_ru.ts @@ -2743,6 +2743,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_sr.ts b/translations/bt_sr.ts index fc580d3d..a5a5aadc 100644 --- a/translations/bt_sr.ts +++ b/translations/bt_sr.ts @@ -2591,6 +2591,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_sv.ts b/translations/bt_sv.ts index 863c01db..6114abcc 100644 --- a/translations/bt_sv.ts +++ b/translations/bt_sv.ts @@ -2771,6 +2771,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_tr.ts b/translations/bt_tr.ts index 62643466..173e2a22 100644 --- a/translations/bt_tr.ts +++ b/translations/bt_tr.ts @@ -2258,6 +2258,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/translations/bt_zh.ts b/translations/bt_zh.ts index 1a8a904d..14d4b0b3 100644 --- a/translations/bt_zh.ts +++ b/translations/bt_zh.ts @@ -2663,6 +2663,35 @@ Log file may contain more details. + + QApplication + + Brewken is already running! + + + + Another instance of Brewken is already running. + +Running two copies of the program at once may lead to data loss. + +Press OK to quit. + + + + Application terminates + + + + The application encountered a fatal error. +Error message: +%1 + + + + The application encountered a fatal error. + + + QObject diff --git a/ui/brewNoteWidget.ui b/ui/brewNoteWidget.ui index c2a501d3..cce241c1 100644 --- a/ui/brewNoteWidget.ui +++ b/ui/brewNoteWidget.ui @@ -84,7 +84,7 @@ - + Qt::CustomContextMenu @@ -95,12 +95,12 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - lineEdit_SG + lineEdit_Sg - + 0 @@ -131,7 +131,7 @@ - + Qt::CustomContextMenu @@ -142,12 +142,12 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - lineEdit_volIntoBK + lineEdit_volIntoBk - + 0 @@ -175,7 +175,7 @@ - + Qt::CustomContextMenu @@ -191,7 +191,7 @@ - + 0 @@ -219,7 +219,7 @@ - + Qt::CustomContextMenu @@ -235,7 +235,7 @@ - + 0 @@ -281,7 +281,7 @@ - + Qt::CustomContextMenu @@ -292,12 +292,12 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - lineEdit_OG + lineEdit_Og - + 0 @@ -325,7 +325,7 @@ - + Qt::CustomContextMenu @@ -347,7 +347,7 @@ - + 0 @@ -375,7 +375,7 @@ - + Qt::CustomContextMenu @@ -397,7 +397,7 @@ - + 0 @@ -422,7 +422,7 @@ - + Qt::CustomContextMenu @@ -441,7 +441,7 @@ - + 0 @@ -487,7 +487,7 @@ - + Qt::CustomContextMenu @@ -495,12 +495,12 @@ FG - lineEdit_FG + lineEdit_Fg - + 0 @@ -528,7 +528,7 @@ - + Qt::CustomContextMenu @@ -536,12 +536,12 @@ Volume - lineEdit_finalVol + lineEdit_finalVolume - + 0 @@ -569,7 +569,7 @@ - + Qt::CustomContextMenu @@ -613,7 +613,7 @@ QFormLayout::ExpandingFieldsGrow - + Percent efficiency into boil kettle @@ -630,7 +630,7 @@ - + Qt::CustomContextMenu @@ -656,7 +656,7 @@ - + Brewhouse efficiency @@ -673,7 +673,7 @@ - + Expected ABV based on recipe OG @@ -707,7 +707,7 @@ - + Yeast attenuation based on yeast specified in recipe @@ -724,7 +724,7 @@ - + Yeast attentuation based on user-reported OG and FG @@ -777,17 +777,9 @@ - BtTemperatureEdit - QLineEdit -
BtLineEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtTemperatureLabel + SmartLabel QLabel -
BtLabel.h
+
widgets/SmartLabel.h
changedSystemOfMeasurementOrScale(PreviousScaleInfo) @@ -798,33 +790,9 @@
BtTextEdit.h
- BtVolumeLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
- - BtDensityLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
- - BtVolumeEdit + SmartLineEdit QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtDensityEdit - QLineEdit -
BtAmountEdit.h
+
widgets/SmartLineEdit.h
lineChanged(PreviousScaleInfo) @@ -834,20 +802,15 @@ QLabel
widgets/BtDigitWidget.h
- - BtDateLabel - QLabel -
BtLabel.h
-
- btLabel_Sg + label_Sg changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_SG + lineEdit_Sg lineChanged(PreviousScaleInfo) @@ -861,9 +824,9 @@ - btLabel_volIntoBk + label_volIntoBk changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_volIntoBK + lineEdit_volIntoBk lineChanged(PreviousScaleInfo) @@ -877,7 +840,7 @@ - btLabel_strikeTemp + label_strikeTemp changedSystemOfMeasurementOrScale(PreviousScaleInfo) lineEdit_strikeTemp lineChanged(PreviousScaleInfo) @@ -893,7 +856,7 @@ - btLabel_mashFinTemp + label_mashFinTemp changedSystemOfMeasurementOrScale(PreviousScaleInfo) lineEdit_mashFinTemp lineChanged(PreviousScaleInfo) @@ -909,9 +872,9 @@ - btLabel_Og + label_Og changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_OG + lineEdit_Og lineChanged(PreviousScaleInfo) @@ -925,7 +888,7 @@ - btLabel_postBoilVol + label_postBoilVol changedSystemOfMeasurementOrScale(PreviousScaleInfo) lineEdit_postBoilVol lineChanged(PreviousScaleInfo) @@ -941,7 +904,7 @@ - btLabel_volIntoFerm + label_volIntoFerm changedSystemOfMeasurementOrScale(PreviousScaleInfo) lineEdit_volIntoFerm lineChanged(PreviousScaleInfo) @@ -957,7 +920,7 @@ - btLabel_pitchTemp + label_pitchTemp changedSystemOfMeasurementOrScale(PreviousScaleInfo) lineEdit_pitchTemp lineChanged(PreviousScaleInfo) @@ -973,9 +936,9 @@ - btLabel_postFermentFg + label_Fg changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_FG + lineEdit_Fg lineChanged(PreviousScaleInfo) @@ -989,9 +952,9 @@ - btLabel_finalVolume + label_finalVolume changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_finalVol + lineEdit_finalVolume lineChanged(PreviousScaleInfo) From f68ac7e59fa0111fd33134371b8095111c10f8d4 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Wed, 29 Mar 2023 22:27:38 +0200 Subject: [PATCH 10/42] SmartLineEdit in equipment editory --- src/EquipmentEditor.cpp | 98 +++++++++++++----------- src/model/BrewNote.cpp | 52 ++++++------- src/model/Equipment.cpp | 34 ++++----- src/utils/TypeLookup.h | 10 ++- src/widgets/SmartLineEdit.cpp | 19 ++++- ui/equipmentEditor.ui | 136 ++++++++-------------------------- 6 files changed, 158 insertions(+), 191 deletions(-) diff --git a/src/EquipmentEditor.cpp b/src/EquipmentEditor.cpp index fd561cea..c2074af6 100644 --- a/src/EquipmentEditor.cpp +++ b/src/EquipmentEditor.cpp @@ -64,7 +64,7 @@ EquipmentEditor::EquipmentEditor(QWidget* parent, bool singleEquipEditor) : Measurement::Unit const * weightUnit = nullptr; Measurement::Unit const * volumeUnit = nullptr; Measurement::getThicknessUnits(&volumeUnit, &weightUnit); - label_absorption->setText(tr("Grain absorption (%1/%2)").arg(volumeUnit->name).arg(weightUnit->name)); + label_grainAbsorption->setText(tr("Grain absorption (%1/%2)").arg(volumeUnit->name).arg(weightUnit->name)); equipmentListModel = new EquipmentListModel(equipmentComboBox); equipmentSortProxyModel = new NamedEntitySortProxyModel(equipmentListModel); @@ -72,12 +72,28 @@ EquipmentEditor::EquipmentEditor(QWidget* parent, bool singleEquipEditor) : obsEquip = nullptr; + this->lineEdit_tunSpecificHeat->init(Equipment::typeLookup.getType(PropertyNames::Equipment::tunSpecificHeat_calGC), *this->label_tunSpecificHeat ); + this->lineEdit_grainAbsorption->init(Equipment::typeLookup.getType(PropertyNames::Equipment::grainAbsorption_LKg ) ); + this->lineEdit_hopUtilization ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::hopUtilization_pct ) , 0); // label_hopUtilization + this->lineEdit_tunWeight ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::tunWeight_kg ), *this->label_tunWeight ); + this->lineEdit_name ->init(Equipment::typeLookup.getType(PropertyNames::NamedEntity::name ) ); + this->lineEdit_boilingPoint ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::boilingPoint_c ), *this->label_boilingPoint , 1); + this->lineEdit_boilTime ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::boilTime_min ), *this->label_boilTime ); + this->lineEdit_batchSize ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::batchSize_l ), *this->label_batchSize ); + this->lineEdit_boilSize ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::boilSize_l ), *this->label_boilSize ); + this->lineEdit_evaporationRate->init(Equipment::typeLookup.getType(PropertyNames::Equipment::evapRate_lHr ), *this->label_evaporationRate ); + this->lineEdit_lauterDeadspace->init(Equipment::typeLookup.getType(PropertyNames::Equipment::lauterDeadspace_l ), *this->label_lauterDeadspace ); + this->lineEdit_topUpKettle ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::topUpKettle_l ), *this->label_topUpKettle ); + this->lineEdit_topUpWater ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::topUpWater_l ), *this->label_topUpWater ); + this->lineEdit_trubChillerLoss->init(Equipment::typeLookup.getType(PropertyNames::Equipment::trubChillerLoss_l ), *this->label_trubChillerLoss ); + this->lineEdit_tunVolume ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::tunVolume_l ), *this->label_tunVolume ); + // Connect all the edit boxen - connect(lineEdit_boilTime, &BtLineEdit::textModified, this, &EquipmentEditor::updateCheckboxRecord ); - connect(lineEdit_evaporationRate, &BtLineEdit::textModified, this, &EquipmentEditor::updateCheckboxRecord ); - connect(lineEdit_topUpWater, &BtLineEdit::textModified, this, &EquipmentEditor::updateCheckboxRecord ); - connect(lineEdit_trubChillerLoss, &BtLineEdit::textModified, this, &EquipmentEditor::updateCheckboxRecord ); - connect(lineEdit_batchSize, &BtLineEdit::textModified, this, &EquipmentEditor::updateCheckboxRecord ); + connect(lineEdit_boilTime, &SmartLineEdit::textModified, this, &EquipmentEditor::updateCheckboxRecord ); + connect(lineEdit_evaporationRate, &SmartLineEdit::textModified, this, &EquipmentEditor::updateCheckboxRecord ); + connect(lineEdit_topUpWater, &SmartLineEdit::textModified, this, &EquipmentEditor::updateCheckboxRecord ); + connect(lineEdit_trubChillerLoss, &SmartLineEdit::textModified, this, &EquipmentEditor::updateCheckboxRecord ); + connect(lineEdit_batchSize, &SmartLineEdit::textModified, this, &EquipmentEditor::updateCheckboxRecord ); // Set up the buttons // Note, per https://wiki.qt.io/New_Signal_Slot_Syntax#Default_arguments_in_slot, the use of a trivial lambda // function to allow use of default argument on newEquipment() slot @@ -271,7 +287,7 @@ void EquipmentEditor::resetAbsorption() { Measurement::getThicknessUnits(&volumeUnit, &weightUnit); double gaCustomUnits = PhysicalConstants::grainAbsorption_Lkg * volumeUnit->fromCanonical(1.0) * weightUnit->toCanonical(1.0).quantity(); - lineEdit_grainAbsorption->setText(gaCustomUnits); + lineEdit_grainAbsorption->setAmount(gaCustomUnits); return; } @@ -292,37 +308,37 @@ void EquipmentEditor::showChanges() { Measurement::Unit const * weightUnit = nullptr; Measurement::Unit const * volumeUnit = nullptr; Measurement::getThicknessUnits( &volumeUnit, &weightUnit ); - label_absorption->setText(tr("Grain absorption (%1/%2)").arg(volumeUnit->name).arg(weightUnit->name)); + this->label_grainAbsorption->setText(tr("Grain absorption (%1/%2)").arg(volumeUnit->name).arg(weightUnit->name)); //equipmentComboBox->setIndexByEquipment(this->obsEquip); - lineEdit_name->setText(this->obsEquip->name()); - lineEdit_name->setCursorPosition(0); - tabWidget_editor->setTabText(0, this->obsEquip->name()); - lineEdit_boilSize->setText(this->obsEquip->boilSize_l()); - - checkBox_calcBoilVolume->blockSignals(true); // Keep next line from emitting a signal and changing this->obsEquip. - checkBox_calcBoilVolume->setCheckState( (this->obsEquip->calcBoilVolume())? Qt::Checked : Qt::Unchecked ); - checkBox_calcBoilVolume->blockSignals(false); - - lineEdit_batchSize ->setText(this->obsEquip->batchSize_l ()); - lineEdit_tunVolume ->setText(this->obsEquip->tunVolume_l ()); - lineEdit_tunWeight ->setText(this->obsEquip->tunWeight_kg ()); - lineEdit_tunSpecificHeat->setText(this->obsEquip->tunSpecificHeat_calGC()); - lineEdit_boilTime ->setText(this->obsEquip->boilTime_min ()); - lineEdit_evaporationRate->setText(this->obsEquip->evapRate_lHr ()); - lineEdit_topUpKettle ->setText(this->obsEquip->topUpKettle_l ()); - lineEdit_topUpWater ->setText(this->obsEquip->topUpWater_l ()); - lineEdit_trubChillerLoss->setText(this->obsEquip->trubChillerLoss_l ()); - lineEdit_lauterDeadspace->setText(this->obsEquip->lauterDeadspace_l ()); - textEdit_notes ->setText(this->obsEquip->notes ()); + this->lineEdit_name ->setText(this->obsEquip->name()); + this->lineEdit_name ->setCursorPosition(0); + this->tabWidget_editor ->setTabText(0, this->obsEquip->name()); + this->lineEdit_boilSize ->setAmount(this->obsEquip->boilSize_l()); + + this->checkBox_calcBoilVolume ->blockSignals(true); // Keep next line from emitting a signal and changing this->obsEquip. + this->checkBox_calcBoilVolume ->setCheckState( (this->obsEquip->calcBoilVolume())? Qt::Checked : Qt::Unchecked ); + this->checkBox_calcBoilVolume ->blockSignals(false); + + this->lineEdit_batchSize ->setAmount(this->obsEquip->batchSize_l ()); + this->lineEdit_tunVolume ->setAmount(this->obsEquip->tunVolume_l ()); + this->lineEdit_tunWeight ->setAmount(this->obsEquip->tunWeight_kg ()); + this->lineEdit_tunSpecificHeat ->setAmount(this->obsEquip->tunSpecificHeat_calGC()); + this->lineEdit_boilTime ->setAmount(this->obsEquip->boilTime_min ()); + this->lineEdit_evaporationRate ->setAmount(this->obsEquip->evapRate_lHr ()); + this->lineEdit_topUpKettle ->setAmount(this->obsEquip->topUpKettle_l ()); + this->lineEdit_topUpWater ->setAmount(this->obsEquip->topUpWater_l ()); + this->lineEdit_trubChillerLoss ->setAmount(this->obsEquip->trubChillerLoss_l ()); + this->lineEdit_lauterDeadspace ->setAmount(this->obsEquip->lauterDeadspace_l ()); + this->textEdit_notes ->setText (this->obsEquip->notes ()); double gaCustomUnits = this->obsEquip->grainAbsorption_LKg() * volumeUnit->fromCanonical(1.0) * weightUnit->toCanonical(1.0).quantity(); - lineEdit_grainAbsorption->setText(gaCustomUnits); + this->lineEdit_grainAbsorption ->setAmount(gaCustomUnits); - lineEdit_boilingPoint ->setText(this->obsEquip->boilingPoint_c ()); - lineEdit_hopUtilization->setText(this->obsEquip->hopUtilization_pct()); - checkBox_defaultEquipment->blockSignals(true); + this->lineEdit_boilingPoint ->setAmount(this->obsEquip->boilingPoint_c ()); + this->lineEdit_hopUtilization ->setAmount(this->obsEquip->hopUtilization_pct()); + this->checkBox_defaultEquipment->blockSignals(true); if (PersistentSettings::value(PersistentSettings::Names::defaultEquipmentKey, -1) == this->obsEquip->key()) { checkBox_defaultEquipment->setCheckState(Qt::Checked); } else { @@ -334,24 +350,22 @@ void EquipmentEditor::showChanges() { } void EquipmentEditor::updateCheckboxRecord() { - int state = checkBox_calcBoilVolume->checkState(); - if ( state == Qt::Checked ) { - double bar = calcBatchSize(); - lineEdit_boilSize->setText(bar); - lineEdit_boilSize->setEnabled(false); + if (Qt::Checked == this->checkBox_calcBoilVolume->checkState()) { + this->lineEdit_boilSize->setAmount(this->calcBatchSize()); + this->lineEdit_boilSize->setEnabled(false); } else { - lineEdit_boilSize->setText(lineEdit_batchSize->toCanonical().quantity()); - lineEdit_boilSize->setEnabled(true); + this->lineEdit_boilSize->setAmount(this->lineEdit_batchSize->toCanonical().quantity()); + this->lineEdit_boilSize->setEnabled(true); } return; } double EquipmentEditor::calcBatchSize() { - double size = lineEdit_batchSize->toCanonical().quantity(); - double topUp = lineEdit_topUpWater->toCanonical().quantity(); + double size = lineEdit_batchSize ->toCanonical().quantity(); + double topUp = lineEdit_topUpWater ->toCanonical().quantity(); double trubLoss = lineEdit_trubChillerLoss->toCanonical().quantity(); double evapRate = lineEdit_evaporationRate->toCanonical().quantity(); - double time = lineEdit_boilTime->toCanonical().quantity(); + double time = lineEdit_boilTime ->toCanonical().quantity(); return size - topUp + trubLoss + (time/60.0)*evapRate; } diff --git a/src/model/BrewNote.cpp b/src/model/BrewNote.cpp index c07ebbbb..31757411 100644 --- a/src/model/BrewNote.cpp +++ b/src/model/BrewNote.cpp @@ -65,37 +65,37 @@ TypeLookup const BrewNote::typeLookup { "BrewNote", { // Note that we need Enums to be treated as ints for the purposes of type lookup - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::abv , BrewNote::m_abv ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::attenuation , BrewNote::m_attenuation ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::boilOff_l , BrewNote::m_boilOff_l , Measurement::PhysicalQuantity::Volume), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::brewDate , BrewNote::m_brewDate , NonPhysicalQuantity::Date), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::brewhouseEff_pct , BrewNote::m_brewhouseEff_pct , NonPhysicalQuantity::Percentage), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::effIntoBK_pct , BrewNote::m_effIntoBK_pct , NonPhysicalQuantity::Percentage), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::fermentDate , BrewNote::m_fermentDate , NonPhysicalQuantity::Date), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::fg , BrewNote::m_fg , Measurement::PhysicalQuantity::Density), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::finalVolume_l , BrewNote::m_finalVolume_l , Measurement::PhysicalQuantity::Volume), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::abv , BrewNote::m_abv , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::attenuation , BrewNote::m_attenuation ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::boilOff_l , BrewNote::m_boilOff_l , Measurement::PhysicalQuantity::Volume ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::brewDate , BrewNote::m_brewDate , NonPhysicalQuantity::Date ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::brewhouseEff_pct , BrewNote::m_brewhouseEff_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::effIntoBK_pct , BrewNote::m_effIntoBK_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::fermentDate , BrewNote::m_fermentDate , NonPhysicalQuantity::Date ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::fg , BrewNote::m_fg , Measurement::PhysicalQuantity::Density ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::finalVolume_l , BrewNote::m_finalVolume_l , Measurement::PhysicalQuantity::Volume ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::mashFinTemp_c , BrewNote::m_mashFinTemp_c , Measurement::PhysicalQuantity::Temperature), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::notes , BrewNote::m_notes ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::og , BrewNote::m_og , Measurement::PhysicalQuantity::Density), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::notes , BrewNote::m_notes ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::og , BrewNote::m_og , Measurement::PhysicalQuantity::Density ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::pitchTemp_c , BrewNote::m_pitchTemp_c , Measurement::PhysicalQuantity::Temperature), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::postBoilVolume_l , BrewNote::m_postBoilVolume_l , Measurement::PhysicalQuantity::Volume), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projABV_pct , BrewNote::m_projABV_pct , NonPhysicalQuantity::Percentage), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projAtten , BrewNote::m_projAtten ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projBoilGrav , BrewNote::m_projBoilGrav , Measurement::PhysicalQuantity::Density), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projEff_pct , BrewNote::m_projEff_pct , NonPhysicalQuantity::Percentage), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projFermPoints , BrewNote::m_projFermPoints ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projFg , BrewNote::m_projFg , Measurement::PhysicalQuantity::Density), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::postBoilVolume_l , BrewNote::m_postBoilVolume_l , Measurement::PhysicalQuantity::Volume ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projABV_pct , BrewNote::m_projABV_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projAtten , BrewNote::m_projAtten ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projBoilGrav , BrewNote::m_projBoilGrav , Measurement::PhysicalQuantity::Density ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projEff_pct , BrewNote::m_projEff_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projFermPoints , BrewNote::m_projFermPoints ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projFg , BrewNote::m_projFg , Measurement::PhysicalQuantity::Density ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projMashFinTemp_c, BrewNote::m_projMashFinTemp_c, Measurement::PhysicalQuantity::Temperature), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projOg , BrewNote::m_projOg , Measurement::PhysicalQuantity::Density), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projPoints , BrewNote::m_projPoints ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projOg , BrewNote::m_projOg , Measurement::PhysicalQuantity::Density ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projPoints , BrewNote::m_projPoints ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projStrikeTemp_c , BrewNote::m_projStrikeTemp_c , Measurement::PhysicalQuantity::Temperature), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projVolIntoBK_l , BrewNote::m_projVolIntoBK_l , Measurement::PhysicalQuantity::Volume), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projVolIntoFerm_l, BrewNote::m_projVolIntoFerm_l, Measurement::PhysicalQuantity::Volume), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::recipeId , BrewNote::m_recipeId ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::sg , BrewNote::m_sg , Measurement::PhysicalQuantity::Density), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projVolIntoBK_l , BrewNote::m_projVolIntoBK_l , Measurement::PhysicalQuantity::Volume ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projVolIntoFerm_l, BrewNote::m_projVolIntoFerm_l, Measurement::PhysicalQuantity::Volume ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::recipeId , BrewNote::m_recipeId ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::sg , BrewNote::m_sg , Measurement::PhysicalQuantity::Density ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::strikeTemp_c , BrewNote::m_strikeTemp_c , Measurement::PhysicalQuantity::Temperature), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::volumeIntoBK_l , BrewNote::m_volumeIntoBK_l , Measurement::PhysicalQuantity::Volume), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::volumeIntoFerm_l , BrewNote::m_volumeIntoFerm_l , Measurement::PhysicalQuantity::Volume), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::volumeIntoBK_l , BrewNote::m_volumeIntoBK_l , Measurement::PhysicalQuantity::Volume ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::volumeIntoFerm_l , BrewNote::m_volumeIntoFerm_l , Measurement::PhysicalQuantity::Volume ), }, // Parent class lookup &NamedEntity::typeLookup diff --git a/src/model/Equipment.cpp b/src/model/Equipment.cpp index 2d71d154..18d31192 100644 --- a/src/model/Equipment.cpp +++ b/src/model/Equipment.cpp @@ -52,23 +52,23 @@ ObjectStore & Equipment::getObjectStoreTypedInstance() const { TypeLookup const Equipment::typeLookup { "Equipment", { - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::batchSize_l , Equipment::m_batchSize_l ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::boilingPoint_c , Equipment::m_boilingPoint_c ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::boilSize_l , Equipment::m_boilSize_l ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::boilTime_min , Equipment::m_boilTime_min ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::calcBoilVolume , Equipment::m_calcBoilVolume ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::evapRate_lHr , Equipment::m_evapRate_lHr ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::evapRate_pctHr , Equipment::m_evapRate_pctHr ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::grainAbsorption_LKg , Equipment::m_grainAbsorption_LKg ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::hopUtilization_pct , Equipment::m_hopUtilization_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::lauterDeadspace_l , Equipment::m_lauterDeadspace_l ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::notes , Equipment::m_notes ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::topUpKettle_l , Equipment::m_topUpKettle_l ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::topUpWater_l , Equipment::m_topUpWater_l ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::trubChillerLoss_l , Equipment::m_trubChillerLoss_l ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::tunSpecificHeat_calGC, Equipment::m_tunSpecificHeat_calGC), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::tunVolume_l , Equipment::m_tunVolume_l ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::tunWeight_kg , Equipment::m_tunWeight_kg ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::batchSize_l , Equipment::m_batchSize_l , Measurement::PhysicalQuantity::Volume ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::boilingPoint_c , Equipment::m_boilingPoint_c , Measurement::PhysicalQuantity::Temperature ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::boilSize_l , Equipment::m_boilSize_l , Measurement::PhysicalQuantity::Volume ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::boilTime_min , Equipment::m_boilTime_min , Measurement::PhysicalQuantity::Time ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::calcBoilVolume , Equipment::m_calcBoilVolume , NonPhysicalQuantity::Bool ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::evapRate_lHr , Equipment::m_evapRate_lHr , Measurement::PhysicalQuantity::Volume ), // The "per hour" bit is fixed, so we simplify + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::evapRate_pctHr , Equipment::m_evapRate_pctHr , NonPhysicalQuantity::Percentage ), // The "per hour" bit is fixed, so we simplify + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::grainAbsorption_LKg , Equipment::m_grainAbsorption_LKg , NonPhysicalQuantity::Dimensionless ), // Not really dimensionless... + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::hopUtilization_pct , Equipment::m_hopUtilization_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::lauterDeadspace_l , Equipment::m_lauterDeadspace_l , Measurement::PhysicalQuantity::Volume ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::notes , Equipment::m_notes ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::topUpKettle_l , Equipment::m_topUpKettle_l , Measurement::PhysicalQuantity::Volume ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::topUpWater_l , Equipment::m_topUpWater_l , Measurement::PhysicalQuantity::Volume ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::trubChillerLoss_l , Equipment::m_trubChillerLoss_l , Measurement::PhysicalQuantity::Volume ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::tunSpecificHeat_calGC, Equipment::m_tunSpecificHeat_calGC, Measurement::PhysicalQuantity::SpecificHeatCapacity), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::tunVolume_l , Equipment::m_tunVolume_l , Measurement::PhysicalQuantity::Volume ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Equipment::tunWeight_kg , Equipment::m_tunWeight_kg , Measurement::PhysicalQuantity::Mass ), }, // Parent class lookup &NamedEntity::typeLookup diff --git a/src/utils/TypeLookup.h b/src/utils/TypeLookup.h index ae68afc6..e008e7ab 100644 --- a/src/utils/TypeLookup.h +++ b/src/utils/TypeLookup.h @@ -24,6 +24,7 @@ #include "BtFieldType.h" #include "utils/BtStringConst.h" +#include "utils/OptionalHelpers.h" /** * \brief Together with \c std::is_enum from \c , the \c is_optional and \c is_optional_enum templates @@ -222,7 +223,14 @@ class TypeLookup { #define PROPERTY_TYPE_LOOKUP_ENTRY(propNameConstVar, memberVar, ...) {&propNameConstVar, TypeInfo::construct(__VA_OPT__ (__VA_ARGS__))} /** - * \brief + * \brief Convenience function for logging */ +template +S & operator<<(S & stream, TypeInfo const & typeInfo) { + stream << + "TypeInfo " << (typeInfo.isOptional() ? "" : "non-") << "optional \"" << typeInfo.typeIndex.name() << + "\" fieldType:" << typeInfo.fieldType; + return stream; +} #endif diff --git a/src/widgets/SmartLineEdit.cpp b/src/widgets/SmartLineEdit.cpp index 7343fe84..1fdc4767 100644 --- a/src/widgets/SmartLineEdit.cpp +++ b/src/widgets/SmartLineEdit.cpp @@ -146,7 +146,6 @@ class SmartLineEdit::impl { return; } - SmartLineEdit & m_self; bool m_initialised; TypeInfo const * m_typeInfo; @@ -172,6 +171,8 @@ void SmartLineEdit::init(TypeInfo const & typeInfo, SmartLabel & buddyLabel, int const defaultPrecision, QString const & maximalDisplayString) { + qDebug() << Q_FUNC_INFO << typeInfo; + // It's a coding error to call this version of init with a NonPhysicalQuantity Q_ASSERT(typeInfo.fieldType && !std::holds_alternative(*typeInfo.fieldType)); @@ -191,6 +192,8 @@ void SmartLineEdit::init(TypeInfo const & typeInfo, void SmartLineEdit::init(TypeInfo const & typeInfo, int const defaultPrecision, QString const & maximalDisplayString) { + qDebug() << Q_FUNC_INFO << typeInfo; + // It's a coding error to call this version of init with anything other than a NonPhysicalQuantity Q_ASSERT(typeInfo.fieldType && std::holds_alternative(*typeInfo.fieldType)); @@ -253,6 +256,20 @@ void SmartLineEdit::setAmount(std::optional amount, std::optional p return; } +template T SmartLineEdit::getValueAs() const { + qDebug() << Q_FUNC_INFO << "Converting" << this->text() << "to" << Measurement::extractRawFromString(this->text()); + return Measurement::extractRawFromString(this->text()); +} +// +// Instantiate the above template function 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.) +// +template int SmartLineEdit::getValueAs() const; +template unsigned int SmartLineEdit::getValueAs() const; +template double SmartLineEdit::getValueAs() const; + + //============================================ Property Getters and Setters ============================================ // Note that we cannot assume init() has yet been run when these are called from (code generated from) a .ui file diff --git a/ui/equipmentEditor.ui b/ui/equipmentEditor.ui index cca16072..724d16e7 100644 --- a/ui/equipmentEditor.ui +++ b/ui/equipmentEditor.ui @@ -98,7 +98,7 @@ - + Qt::CustomContextMenu @@ -111,7 +111,7 @@ - + Qt::CustomContextMenu @@ -124,7 +124,7 @@ - + Name @@ -144,21 +144,21 @@ - + boilSize_l - + batchSize_l - + 0 @@ -194,7 +194,7 @@ - + Qt::CustomContextMenu @@ -214,7 +214,7 @@ - + Qt::CustomContextMenu @@ -227,14 +227,14 @@ - + evapRate_lHr - + Qt::CustomContextMenu @@ -247,14 +247,14 @@ - + topUpWater_l - + Qt::CustomContextMenu @@ -267,14 +267,14 @@ - + topUpKettle_l - + Qt::CustomContextMenu @@ -287,14 +287,14 @@ - + trubChillerLoss_l - + Qt::CustomContextMenu @@ -307,7 +307,7 @@ - + lauterDeadspace_l @@ -328,7 +328,7 @@ - + Grain absorption (L/kg) @@ -348,14 +348,14 @@ - + grainAbsorption_LKg - + Qt::CustomContextMenu @@ -368,7 +368,7 @@ - + Qt::CustomContextMenu @@ -381,7 +381,7 @@ - + Qt::CustomContextMenu @@ -394,7 +394,7 @@ - + 0 @@ -410,7 +410,7 @@ - + hopUtilization_pct @@ -430,7 +430,7 @@ - + boilingPoint_c @@ -450,21 +450,21 @@ - + tunWeight_kg - + Specific Heat (Cal/(g*C)) - + tunSpecificHeat_calGC @@ -566,89 +566,17 @@ - BtGenericEdit - QLineEdit -
BtLineEdit.h
-
- - BtTemperatureEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtTemperatureLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
- - BtStringEdit - QLineEdit -
BtLineEdit.h
-
- - BtTimeEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtTimeLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
- - BtMassEdit - QLineEdit -
BtAmountEdit.h
- - textModified() - lineChanged() - lineChanged(PreviousScaleInfo) - -
- - BtMassLabel + SmartLabel QLabel -
BtLabel.h
+
widgets/SmartLabel.h
changedSystemOfMeasurementOrScale(PreviousScaleInfo) - popContextMenu(QPoint)
- BtVolumeLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
- - BtVolumeEdit + SmartLineEdit QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtDimensionlessEdit - QLineEdit -
BtLineEdit.h
- - lineChanged(PreviousScaleInfo) - +
widgets/SmartLineEdit.h
From 99ee60d56ff39e7a2d462ab384ef4bc7f4ecda59 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sat, 1 Apr 2023 03:10:31 +0200 Subject: [PATCH 11/42] Fermentable editor to use SmartLineEdit etc --- src/BtAmountEdit.cpp | 11 +--- src/BtFieldType.h | 1 - src/FermentableEditor.cpp | 47 ++++++++------ src/UiAmountWithUnits.cpp | 12 +--- src/UiAmountWithUnits.h | 21 ++----- src/model/Fermentable.cpp | 74 +++++++++++----------- src/model/Fermentable.h | 2 +- src/model/Hop.cpp | 48 +++++++-------- src/model/Inventory.cpp | 2 +- src/widgets/BtAmountDigitWidget.cpp | 11 +--- src/widgets/BtAmountDigitWidget.h | 10 --- src/widgets/BtDigitWidget.cpp | 3 + src/widgets/BtDigitWidget.h | 2 + src/widgets/SmartLineEdit.cpp | 95 ++++++++++++++++++++--------- src/widgets/SmartLineEdit.h | 6 +- ui/fermentableEditor.ui | 82 ++++++------------------- 16 files changed, 198 insertions(+), 229 deletions(-) diff --git a/src/BtAmountEdit.cpp b/src/BtAmountEdit.cpp index a033bec8..01c53ba1 100644 --- a/src/BtAmountEdit.cpp +++ b/src/BtAmountEdit.cpp @@ -47,15 +47,6 @@ BtAmountEdit::BtAmountEdit(QWidget *parent, BtAmountEdit::~BtAmountEdit() = default; -///QString BtAmountEdit::getWidgetText() const { -/// return this->text(); -///} -/// -///void BtAmountEdit::setWidgetText(QString text) { -/// this->QLineEdit::setText(text); -/// return; -///} - Measurement::Amount BtAmountEdit::toCanonical() const { return this->rawToCanonical(this->text()); } @@ -171,7 +162,7 @@ void BtAmountEdit::lineChanged(PreviousScaleInfo previousScaleInfo) { return; } - this->QLineEdit::setText(this->correctEnteredText(this->text(), previousScaleInfo)); + this->QLineEdit::setText(this->correctEnteredText(this->text(), 3, previousScaleInfo)); if (sender() == this) { emit textModified(); diff --git a/src/BtFieldType.h b/src/BtFieldType.h index e835169e..2ab6b4b4 100644 --- a/src/BtFieldType.h +++ b/src/BtFieldType.h @@ -35,7 +35,6 @@ enum class NonPhysicalQuantity { Percentage, Bool, /** - * TODO Kill this! * \brief This is for a number that has no units, not even pseudo ones. It is currently a bit over-used -- ie there * are places we are using this (typically via BtNumberOnlyEdit) where we probably should be using a * \c PhysicalQuantity. We should fix these over time. diff --git a/src/FermentableEditor.cpp b/src/FermentableEditor.cpp index d22fc0c4..973428a2 100644 --- a/src/FermentableEditor.cpp +++ b/src/FermentableEditor.cpp @@ -42,6 +42,19 @@ FermentableEditor::FermentableEditor(QWidget* parent) : Fermentable::typeStringMapping.enumToString(ii)); } + this->lineEdit_name ->init(Fermentable::typeLookup.getType(PropertyNames::NamedEntity::name ) ); + this->lineEdit_color ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::color_srm ), *this->label_color ); + this->lineEdit_diastaticPower->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::diastaticPower_lintner), *this->label_diastaticPower ); + this->lineEdit_coarseFineDiff->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::coarseFineDiff_pct ) , 0); + this->lineEdit_ibuGalPerLb ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::ibuGalPerLb ) , 0); + this->lineEdit_maxInBatch ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::maxInBatch_pct ) , 0); + this->lineEdit_moisture ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::moisture_pct ) , 0); + this->lineEdit_protein ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::protein_pct ) , 0); + this->lineEdit_yield ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::yield_pct ) , 1); + this->lineEdit_inventory ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::amount ), *this->label_inventory ); + this->lineEdit_origin ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::origin ) ); + this->lineEdit_supplier ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::supplier ) ); + connect(pushButton_new, &QAbstractButton::clicked, this, &FermentableEditor::clickedNewFermentable); connect(pushButton_save, &QAbstractButton::clicked, this, &FermentableEditor::save); connect(pushButton_cancel, &QAbstractButton::clicked, this, &FermentableEditor::clearAndClose); @@ -128,23 +141,23 @@ void FermentableEditor::showChanges(QMetaProperty* metaProp) { return; } } - if (updateAll || propName == PropertyNames::NamedEntity::name ) { lineEdit_name ->setText(obsFerm->name() ); // Continues to next line - lineEdit_name->setCursorPosition(0); tabWidget_editor->setTabText(0, obsFerm->name()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::NamedEntityWithInventory::inventory) { lineEdit_inventory ->setText(obsFerm->inventory() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::yield_pct ) { lineEdit_yield ->setText(obsFerm->yield_pct() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::color_srm ) { lineEdit_color ->setText(obsFerm->color_srm(), 0 ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::addAfterBoil ) { checkBox_addAfterBoil ->setCheckState(obsFerm->addAfterBoil() ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::origin ) { lineEdit_origin ->setText(obsFerm->origin() ); lineEdit_origin ->setCursorPosition(0); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::supplier ) { lineEdit_supplier ->setText(obsFerm->supplier() ); lineEdit_supplier->setCursorPosition(0); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::coarseFineDiff_pct ) { lineEdit_coarseFineDiff->setText(obsFerm->coarseFineDiff_pct() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::moisture_pct ) { lineEdit_moisture ->setText(obsFerm->moisture_pct() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::diastaticPower_lintner) { lineEdit_diastaticPower->setText(obsFerm->diastaticPower_lintner()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::protein_pct ) { lineEdit_protein ->setText(obsFerm->protein_pct() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::maxInBatch_pct ) { lineEdit_maxInBatch ->setText(obsFerm->maxInBatch_pct() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::recommendMash ) { checkBox_recommendMash ->setCheckState(obsFerm->recommendMash() ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::isMashed ) { checkBox_isMashed ->setCheckState(obsFerm->isMashed() ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::ibuGalPerLb ) { lineEdit_ibuGalPerLb ->setText(obsFerm->ibuGalPerLb() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::notes ) { textEdit_notes ->setPlainText(obsFerm->notes() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::NamedEntity::name ) { lineEdit_name ->setText (obsFerm->name() ); // Continues to next line + lineEdit_name->setCursorPosition(0); tabWidget_editor->setTabText(0, obsFerm->name()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::NamedEntityWithInventory::inventory) { lineEdit_inventory ->setAmount (obsFerm->inventory() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::yield_pct ) { lineEdit_yield ->setAmount (obsFerm->yield_pct() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::color_srm ) { lineEdit_color ->setAmount (obsFerm->color_srm(), 0 ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::addAfterBoil ) { checkBox_addAfterBoil ->setCheckState(obsFerm->addAfterBoil() ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::origin ) { lineEdit_origin ->setText (obsFerm->origin() ); lineEdit_origin ->setCursorPosition(0); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::supplier ) { lineEdit_supplier ->setText (obsFerm->supplier() ); lineEdit_supplier->setCursorPosition(0); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::coarseFineDiff_pct ) { lineEdit_coarseFineDiff->setAmount (obsFerm->coarseFineDiff_pct() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::moisture_pct ) { lineEdit_moisture ->setAmount (obsFerm->moisture_pct() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::diastaticPower_lintner) { lineEdit_diastaticPower->setAmount (obsFerm->diastaticPower_lintner()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::protein_pct ) { lineEdit_protein ->setAmount (obsFerm->protein_pct() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::maxInBatch_pct ) { lineEdit_maxInBatch ->setAmount (obsFerm->maxInBatch_pct() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::recommendMash ) { checkBox_recommendMash ->setCheckState(obsFerm->recommendMash() ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::isMashed ) { checkBox_isMashed ->setCheckState(obsFerm->isMashed() ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::ibuGalPerLb ) { lineEdit_ibuGalPerLb ->setAmount (obsFerm->ibuGalPerLb() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::notes ) { textEdit_notes ->setPlainText (obsFerm->notes() ); if (!updateAll) { return; } } return; } diff --git a/src/UiAmountWithUnits.cpp b/src/UiAmountWithUnits.cpp index 5c04cf2c..e48a6bab 100644 --- a/src/UiAmountWithUnits.cpp +++ b/src/UiAmountWithUnits.cpp @@ -234,7 +234,9 @@ QString UiAmountWithUnits::displayAmount(double amount, int precision) const { ); } -QString UiAmountWithUnits::correctEnteredText(QString const & enteredText, PreviousScaleInfo previousScaleInfo) { +QString UiAmountWithUnits::correctEnteredText(QString const & enteredText, + int precision, + PreviousScaleInfo previousScaleInfo) { QString correctedText; qDebug() << Q_FUNC_INFO << "enteredText:" << enteredText; @@ -247,14 +249,6 @@ QString UiAmountWithUnits::correctEnteredText(QString const & enteredText, Previ // amount (aka to SI) and then into the unit we want. Measurement::Amount amountAsCanonical = this->pimpl->toCanonical(enteredText, previousScaleInfo); - // - // .:TBD:. This seems like a hack we should do away with... - // - Measurement::PhysicalQuantity physicalQuantity = this->getPhysicalQuantity(); - int precision = 3; - if (physicalQuantity == Measurement::PhysicalQuantity::Color) { - precision = 0; - } correctedText = this->displayAmount(amountAsCanonical.quantity(), precision); qDebug() << Q_FUNC_INFO << "Interpreted" << enteredText << "as" << amountAsCanonical << "and corrected to" << correctedText << diff --git a/src/UiAmountWithUnits.h b/src/UiAmountWithUnits.h index 1e408bdd..3c920d09 100644 --- a/src/UiAmountWithUnits.h +++ b/src/UiAmountWithUnits.h @@ -64,21 +64,6 @@ class UiAmountWithUnits { Measurement::PhysicalQuantities const physicalQuantities); virtual ~UiAmountWithUnits(); -/// /** -/// * \brief A class inheriting from this class is also expected to also inherit from a \c QWidget such as \c QLabel or -/// * \c QLineEdit. We would like to be able to access the text() member function of that parent class in parts -/// * of our own implementation. This is a bit tricky as \c QLabel::text() and \c QLineEdit::text() are actually -/// * unrelated, despite both having the same signature. We therefore require child classes to implement this -/// * wrapper function that returns the value of \c text() from their other superclass. -/// */ -/// virtual QString getWidgetText() const = 0; -/// -/// /** -/// * \brief Similar to \c getText(), this allows this base class to access \c QLabel::setText() or -/// * \c QLineEdit::setText() in the subclass that also inherits from \c QLabel or \c QLineEdit. -/// */ -/// virtual void setWidgetText(QString text) = 0; - /** * \brief Returns what type of field this is - except that, if it is \c Mixed2PhysicalQuantities, will one of the two * possible \c Measurement::PhysicalQuantity values depending on the value of \c this->units. @@ -148,6 +133,7 @@ class UiAmountWithUnits { * \brief Use this when you want to do something with the returned QString * * \param amount Must be in canonical units eg kilograms for mass, liters for volume + * \param precision Number of decimals to show .:TBD:. Remove default value here? */ [[nodiscard]] QString displayAmount(double amount, int precision = 3) const; @@ -156,11 +142,14 @@ class UiAmountWithUnits { * to show US Customary volumes and user enters an amount in liters (aka litres) then we need to convert it to * display in pints or quarts etc. * \param enteredText Typically retrieved by caller from \c QLabel::text() or \c QLineEdit::text() + * \param precision Number of decimals to show * \param previousScaleInfo * * \return Corrected text that caller should typically pass back to \c QLabel::setText() or \c QLineEdit::setText() */ - [[nodiscard]] QString correctEnteredText(QString const & enteredText, PreviousScaleInfo previousScaleInfo); + [[nodiscard]] QString correctEnteredText(QString const & enteredText, + int precision, + PreviousScaleInfo previousScaleInfo); protected: /// /** diff --git a/src/model/Fermentable.cpp b/src/model/Fermentable.cpp index c49509e6..403fbdba 100644 --- a/src/model/Fermentable.cpp +++ b/src/model/Fermentable.cpp @@ -115,39 +115,39 @@ TypeLookup const Fermentable::typeLookup { "Fermentable", { PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::type , Fermentable::m_type ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::amount , Fermentable::m_amount ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::amount , Fermentable::m_amount , Measurement::PqEitherMassOrVolume ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::amountIsWeight , Fermentable::m_amountIsWeight ), // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::yield_pct , Fermentable::m_yield_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::color_srm , Fermentable::m_color_srm ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::addAfterBoil , Fermentable::m_addAfterBoil ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::origin , Fermentable::m_origin ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::supplier , Fermentable::m_supplier ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::yield_pct , Fermentable::m_yield_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::color_srm , Fermentable::m_color_srm , Measurement::PhysicalQuantity::Color ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::addAfterBoil , Fermentable::m_addAfterBoil , NonPhysicalQuantity::Bool ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::origin , Fermentable::m_origin , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::supplier , Fermentable::m_supplier , NonPhysicalQuantity::String ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::notes , Fermentable::m_notes ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::coarseFineDiff_pct , Fermentable::m_coarseFineDiff_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::moisture_pct , Fermentable::m_moisture_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::diastaticPower_lintner, Fermentable::m_diastaticPower_lintner), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::protein_pct , Fermentable::m_protein_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::maxInBatch_pct , Fermentable::m_maxInBatch_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::recommendMash , Fermentable::m_recommendMash ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::ibuGalPerLb , Fermentable::m_ibuGalPerLb ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::isMashed , Fermentable::m_isMashed ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::coarseFineDiff_pct , Fermentable::m_coarseFineDiff_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::moisture_pct , Fermentable::m_moisture_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::diastaticPower_lintner, Fermentable::m_diastaticPower_lintner, Measurement::PhysicalQuantity::DiastaticPower), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::protein_pct , Fermentable::m_protein_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::maxInBatch_pct , Fermentable::m_maxInBatch_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::recommendMash , Fermentable::m_recommendMash , NonPhysicalQuantity::Bool ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::ibuGalPerLb , Fermentable::m_ibuGalPerLb , NonPhysicalQuantity::Dimensionless ), // Not really dimensionless... + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::isMashed , Fermentable::m_isMashed , NonPhysicalQuantity::Bool ), // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::grainGroup , Fermentable::m_grainGroup ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::producer , Fermentable::m_producer ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::productId , Fermentable::m_productId ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::fineGrindYield_pct , Fermentable::m_fineGrindYield_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::coarseGrindYield_pct , Fermentable::m_coarseGrindYield_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::potentialYield_sg , Fermentable::m_potentialYield_sg ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::alphaAmylase_dextUnits, Fermentable::m_alphaAmylase_dextUnits), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::kolbachIndex_pct , Fermentable::m_kolbachIndex_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::hardnessPrpGlassy_pct , Fermentable::m_hardnessPrpGlassy_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::hardnessPrpHalf_pct , Fermentable::m_hardnessPrpHalf_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::hardnessPrpMealy_pct , Fermentable::m_hardnessPrpMealy_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::kernelSizePrpPlump_pct, Fermentable::m_kernelSizePrpPlump_pct), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::kernelSizePrpThin_pct , Fermentable::m_kernelSizePrpThin_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::friability_pct , Fermentable::m_friability_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::di_ph , Fermentable::m_di_ph ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::viscosity_cP , Fermentable::m_viscosity_cP ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::producer , Fermentable::m_producer , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::productId , Fermentable::m_productId , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::fineGrindYield_pct , Fermentable::m_fineGrindYield_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::coarseGrindYield_pct , Fermentable::m_coarseGrindYield_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::potentialYield_sg , Fermentable::m_potentialYield_sg , Measurement::PhysicalQuantity::Density ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::alphaAmylase_dextUnits, Fermentable::m_alphaAmylase_dextUnits, NonPhysicalQuantity::Dimensionless ), // Not really dimensionless... + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::kolbachIndex_pct , Fermentable::m_kolbachIndex_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::hardnessPrpGlassy_pct , Fermentable::m_hardnessPrpGlassy_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::hardnessPrpHalf_pct , Fermentable::m_hardnessPrpHalf_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::hardnessPrpMealy_pct , Fermentable::m_hardnessPrpMealy_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::kernelSizePrpPlump_pct, Fermentable::m_kernelSizePrpPlump_pct, NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::kernelSizePrpThin_pct , Fermentable::m_kernelSizePrpThin_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::friability_pct , Fermentable::m_friability_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::di_ph , Fermentable::m_di_ph , Measurement::PhysicalQuantity::Acidity ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::viscosity_cP , Fermentable::m_viscosity_cP , Measurement::PhysicalQuantity::Viscosity ), }, // Parent class lookup. NB: NamedEntityWithInventory not NamedEntity! &NamedEntityWithInventory::typeLookup @@ -347,7 +347,7 @@ double Fermentable::equivSucrose_kg() const { //============================================= "SETTER" MEMBER FUNCTIONS ============================================== void Fermentable::setType (Type const val) { this->setAndNotify(PropertyNames::Fermentable::type , this->m_type , val); } -void Fermentable::setAddAfterBoil (bool const val) { this->setAndNotify(PropertyNames::Fermentable::addAfterBoil , this->m_addAfterBoil , val); } +void Fermentable::setAddAfterBoil (bool const val) { this->setAndNotify(PropertyNames::Fermentable::addAfterBoil , this->m_addAfterBoil , val); } void Fermentable::setOrigin (QString const & val) { this->setAndNotify(PropertyNames::Fermentable::origin , this->m_origin , val); } void Fermentable::setSupplier (QString const & val) { this->setAndNotify(PropertyNames::Fermentable::supplier , this->m_supplier , val); } void Fermentable::setNotes (QString const & val) { this->setAndNotify(PropertyNames::Fermentable::notes , this->m_notes , val); } @@ -356,13 +356,13 @@ void Fermentable::setIsMashed (bool const va void Fermentable::setIbuGalPerLb (double const val) { this->setAndNotify(PropertyNames::Fermentable::ibuGalPerLb , this->m_ibuGalPerLb , val); } void Fermentable::setAmount (double const val) { this->setAndNotify(PropertyNames::Fermentable::amount , this->m_amount , this->enforceMin (val, "amount")); } void Fermentable::setAmountIsWeight (bool const val) { this->setAndNotify(PropertyNames::Fermentable::amountIsWeight , this->m_amountIsWeight , val); } // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ -void Fermentable::setYield_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::yield_pct , this->m_yield_pct , this->enforceMinAndMax(val, "amount", 0.0, 100.0)); } -void Fermentable::setColor_srm (double const val) { this->setAndNotify(PropertyNames::Fermentable::color_srm , this->m_color_srm , this->enforceMin (val, "color")); } -void Fermentable::setCoarseFineDiff_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::coarseFineDiff_pct , this->m_coarseFineDiff_pct , this->enforceMinAndMax(val, "coarseFineDiff", 0.0, 100.0)); } -void Fermentable::setMoisture_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::moisture_pct , this->m_moisture_pct , this->enforceMinAndMax(val, "moisture", 0.0, 100.0)); } -void Fermentable::setDiastaticPower_lintner(double const val) { this->setAndNotify(PropertyNames::Fermentable::diastaticPower_lintner, this->m_diastaticPower_lintner , this->enforceMin (val, "diastatic power")); } -void Fermentable::setProtein_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::protein_pct , this->m_protein_pct , this->enforceMinAndMax(val, "protein", 0.0, 100.0)); } -void Fermentable::setMaxInBatch_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::maxInBatch_pct , this->m_maxInBatch_pct , this->enforceMinAndMax(val, "max in batch", 0.0, 100.0)); } +void Fermentable::setYield_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::yield_pct , this->m_yield_pct , this->enforceMinAndMax(val, "amount", 0.0, 100.0)); } +void Fermentable::setColor_srm (double const val) { this->setAndNotify(PropertyNames::Fermentable::color_srm , this->m_color_srm , this->enforceMin (val, "color")); } +void Fermentable::setCoarseFineDiff_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::coarseFineDiff_pct , this->m_coarseFineDiff_pct , this->enforceMinAndMax(val, "coarseFineDiff", 0.0, 100.0)); } +void Fermentable::setMoisture_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::moisture_pct , this->m_moisture_pct , this->enforceMinAndMax(val, "moisture", 0.0, 100.0)); } +void Fermentable::setDiastaticPower_lintner(double const val) { this->setAndNotify(PropertyNames::Fermentable::diastaticPower_lintner, this->m_diastaticPower_lintner, this->enforceMin (val, "diastatic power")); } +void Fermentable::setProtein_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::protein_pct , this->m_protein_pct , this->enforceMinAndMax(val, "protein", 0.0, 100.0)); } +void Fermentable::setMaxInBatch_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::maxInBatch_pct , this->m_maxInBatch_pct , this->enforceMinAndMax(val, "max in batch", 0.0, 100.0)); } // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ void Fermentable::setGrainGroup (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::grainGroup , this->m_grainGroup , val ); } void Fermentable::setGrainGroupAsInt (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::grainGroup , this->m_grainGroup , castFromOptInt(val)); } diff --git a/src/model/Fermentable.h b/src/model/Fermentable.h index 7095e827..a4268b55 100644 --- a/src/model/Fermentable.h +++ b/src/model/Fermentable.h @@ -207,7 +207,7 @@ class Fermentable : public NamedEntityWithInventory { Q_PROPERTY(double coarseFineDiff_pct READ coarseFineDiff_pct WRITE setCoarseFineDiff_pct ) //! \brief The moisture in pct. Q_PROPERTY(double moisture_pct READ moisture_pct WRITE setMoisture_pct ) - //! \brief The diastatic power in Lintner. + //! \brief The diastatic power in Lintner. .:TODO:. Should probably make this optional and change all current 0 values to NULL Q_PROPERTY(double diastaticPower_lintner READ diastaticPower_lintner WRITE setDiastaticPower_lintner ) //! \brief The percent protein. Q_PROPERTY(double protein_pct READ protein_pct WRITE setProtein_pct ) diff --git a/src/model/Hop.cpp b/src/model/Hop.cpp index a901e3e9..917e794d 100644 --- a/src/model/Hop.cpp +++ b/src/model/Hop.cpp @@ -154,32 +154,32 @@ TypeLookup const Hop::typeLookup { PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::use , Hop::m_use ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::type , Hop::m_type ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::form , Hop::m_form ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::alpha_pct , Hop::m_alpha_pct , NonPhysicalQuantity::Percentage), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::amount_kg , Hop::m_amount_kg ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::time_min , Hop::m_time_min ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::alpha_pct , Hop::m_alpha_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::amount_kg , Hop::m_amount_kg , Measurement::PhysicalQuantity::Mass ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::time_min , Hop::m_time_min , Measurement::PhysicalQuantity::Time ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::notes , Hop::m_notes ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::beta_pct , Hop::m_beta_pct , NonPhysicalQuantity::Percentage), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::hsi_pct , Hop::m_hsi_pct , NonPhysicalQuantity::Percentage), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::origin , Hop::m_origin ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::substitutes , Hop::m_substitutes ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::humulene_pct , Hop::m_humulene_pct , NonPhysicalQuantity::Percentage), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::caryophyllene_pct , Hop::m_caryophyllene_pct , NonPhysicalQuantity::Percentage), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::cohumulone_pct , Hop::m_cohumulone_pct , NonPhysicalQuantity::Percentage), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::myrcene_pct , Hop::m_myrcene_pct , NonPhysicalQuantity::Percentage), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::beta_pct , Hop::m_beta_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::hsi_pct , Hop::m_hsi_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::origin , Hop::m_origin , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::substitutes , Hop::m_substitutes , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::humulene_pct , Hop::m_humulene_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::caryophyllene_pct , Hop::m_caryophyllene_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::cohumulone_pct , Hop::m_cohumulone_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::myrcene_pct , Hop::m_myrcene_pct , NonPhysicalQuantity::Percentage ), // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::producer , Hop::m_producer ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::product_id , Hop::m_product_id ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::year , Hop::m_year ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::total_oil_ml_per_100g, Hop::m_total_oil_ml_per_100g), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::farnesene_pct , Hop::m_farnesene_pct , NonPhysicalQuantity::Percentage), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::geraniol_pct , Hop::m_geraniol_pct , NonPhysicalQuantity::Percentage), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::b_pinene_pct , Hop::m_b_pinene_pct , NonPhysicalQuantity::Percentage), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::linalool_pct , Hop::m_linalool_pct , NonPhysicalQuantity::Percentage), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::limonene_pct , Hop::m_limonene_pct , NonPhysicalQuantity::Percentage), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::nerol_pct , Hop::m_nerol_pct , NonPhysicalQuantity::Percentage), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::pinene_pct , Hop::m_pinene_pct , NonPhysicalQuantity::Percentage), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::polyphenols_pct , Hop::m_polyphenols_pct , NonPhysicalQuantity::Percentage), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::xanthohumol_pct , Hop::m_xanthohumol_pct , NonPhysicalQuantity::Percentage), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::producer , Hop::m_producer , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::product_id , Hop::m_product_id , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::year , Hop::m_year , NonPhysicalQuantity::Dimensionless), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::total_oil_ml_per_100g, Hop::m_total_oil_ml_per_100g, NonPhysicalQuantity::Dimensionless), // Not really dimensionless... + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::farnesene_pct , Hop::m_farnesene_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::geraniol_pct , Hop::m_geraniol_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::b_pinene_pct , Hop::m_b_pinene_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::linalool_pct , Hop::m_linalool_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::limonene_pct , Hop::m_limonene_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::nerol_pct , Hop::m_nerol_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::pinene_pct , Hop::m_pinene_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::polyphenols_pct , Hop::m_polyphenols_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::xanthohumol_pct , Hop::m_xanthohumol_pct , NonPhysicalQuantity::Percentage ), }, // Parent class lookup. NB: NamedEntityWithInventory not NamedEntity! &NamedEntityWithInventory::typeLookup diff --git a/src/model/Inventory.cpp b/src/model/Inventory.cpp index b3d72d46..9a6efb9c 100644 --- a/src/model/Inventory.cpp +++ b/src/model/Inventory.cpp @@ -92,7 +92,7 @@ TypeLookup const Inventory::typeLookup { "Inventory", { // Note that we need Enums to be treated as ints for the purposes of type lookup - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Inventory::amount , Inventory::impl::amount), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Inventory::amount , Inventory::impl::amount, Measurement::PqEitherMassOrVolume), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Inventory::id , Inventory::impl::id ), }, // Parent class lookup diff --git a/src/widgets/BtAmountDigitWidget.cpp b/src/widgets/BtAmountDigitWidget.cpp index 9c9f8273..a5173f29 100644 --- a/src/widgets/BtAmountDigitWidget.cpp +++ b/src/widgets/BtAmountDigitWidget.cpp @@ -27,17 +27,8 @@ BtAmountDigitWidget::BtAmountDigitWidget(QWidget * parent, BtAmountDigitWidget::~BtAmountDigitWidget() = default; -///QString BtAmountDigitWidget::getWidgetText() const { -/// return this->text(); -///} -/// -///void BtAmountDigitWidget::setWidgetText(QString text) { -/// this->QLabel::setText(text); -/// return; -///} - void BtAmountDigitWidget::displayChanged(PreviousScaleInfo previousScaleInfo) { - this->QLabel::setText(this->correctEnteredText(this->text(), previousScaleInfo)); + this->QLabel::setText(this->correctEnteredText(this->text(), this->getPrecision(), previousScaleInfo)); return; } diff --git a/src/widgets/BtAmountDigitWidget.h b/src/widgets/BtAmountDigitWidget.h index f1bbcfb6..f8961e51 100644 --- a/src/widgets/BtAmountDigitWidget.h +++ b/src/widgets/BtAmountDigitWidget.h @@ -42,16 +42,6 @@ class BtAmountDigitWidget : public BtDigitWidget, public UiAmountWithUnits { Measurement::PhysicalQuantities const physicalQuantities); virtual ~BtAmountDigitWidget(); -/// /** -/// * \see \c UiAmountWithUnits for what this member function needs to do -/// */ -/// virtual QString getWidgetText() const; -/// -/// /** -/// * \see \c UiAmountWithUnits for what this member function needs to do -/// */ -/// virtual void setWidgetText(QString text); - public slots: /** * \brief Received from \c BtLabel when the user has change \c UnitSystem diff --git a/src/widgets/BtDigitWidget.cpp b/src/widgets/BtDigitWidget.cpp index 62ed6cd8..77297599 100644 --- a/src/widgets/BtDigitWidget.cpp +++ b/src/widgets/BtDigitWidget.cpp @@ -226,6 +226,9 @@ template int BtDigitWidget::getValueAs() const; template unsigned int BtDigitWidget::getValueAs() const; template double BtDigitWidget::getValueAs() const; +int BtDigitWidget::getPrecision() const { + return this->pimpl->m_lastPrec; +} BtGenericDigit::BtGenericDigit(QWidget* parent) : BtDigitWidget{parent, NonPhysicalQuantity::Count} { diff --git a/src/widgets/BtDigitWidget.h b/src/widgets/BtDigitWidget.h index 276fb590..966c0d64 100644 --- a/src/widgets/BtDigitWidget.h +++ b/src/widgets/BtDigitWidget.h @@ -87,6 +87,8 @@ class BtDigitWidget : public QLabel { template T getValueAs() const; protected: + int getPrecision() const; + BtFieldType fieldType; private: diff --git a/src/widgets/SmartLineEdit.cpp b/src/widgets/SmartLineEdit.cpp index 1fdc4767..b1f8de60 100644 --- a/src/widgets/SmartLineEdit.cpp +++ b/src/widgets/SmartLineEdit.cpp @@ -47,7 +47,7 @@ class SmartLineEdit::impl { m_typeInfo {nullptr}, m_buddyLabel {nullptr}, m_uiAmountWithUnits {nullptr}, - m_defaultPrecision {3}, + m_precision {3}, m_maximalDisplayString{"100.000 srm"}, m_desiredWidthInPixels{0}, m_editField {""}, @@ -110,14 +110,14 @@ class SmartLineEdit::impl { */ void init(TypeInfo const & typeInfo, SmartLabel * buddyLabel, - int const defaultPrecision, + int const precision, QString const & maximalDisplayString) { // It's a coding error to call this function twice on the same object, ie we should only initialise something once! Q_ASSERT(!this->m_initialised); this->m_typeInfo = &typeInfo; this->m_buddyLabel = buddyLabel; - this->m_defaultPrecision = defaultPrecision; + this->m_precision = precision; this->m_maximalDisplayString = maximalDisplayString; this->m_initialised = true; @@ -151,7 +151,7 @@ class SmartLineEdit::impl { TypeInfo const * m_typeInfo; SmartLabel * m_buddyLabel; std::unique_ptr m_uiAmountWithUnits; - int m_defaultPrecision; + int m_precision; QString m_maximalDisplayString; int m_desiredWidthInPixels; // .:TBD:. This is a bit ugly. We keep our own copies of fields that exist in UiAmountWithUnits because we get given @@ -169,7 +169,7 @@ SmartLineEdit::~SmartLineEdit() = default; void SmartLineEdit::init(TypeInfo const & typeInfo, SmartLabel & buddyLabel, - int const defaultPrecision, + int const precision, QString const & maximalDisplayString) { qDebug() << Q_FUNC_INFO << typeInfo; @@ -185,19 +185,19 @@ void SmartLineEdit::init(TypeInfo const & typeInfo, if (!this->pimpl->m_editField .isEmpty()) { this->pimpl->m_uiAmountWithUnits->setEditField (this->pimpl->m_editField ); } if (!this->pimpl->m_configSection.isEmpty()) { this->pimpl->m_uiAmountWithUnits->setConfigSection(this->pimpl->m_configSection); } - this->pimpl->init(typeInfo, &buddyLabel, defaultPrecision, maximalDisplayString); + this->pimpl->init(typeInfo, &buddyLabel, precision, maximalDisplayString); return; } void SmartLineEdit::init(TypeInfo const & typeInfo, - int const defaultPrecision, + int const precision, QString const & maximalDisplayString) { qDebug() << Q_FUNC_INFO << typeInfo; // It's a coding error to call this version of init with anything other than a NonPhysicalQuantity Q_ASSERT(typeInfo.fieldType && std::holds_alternative(*typeInfo.fieldType)); - this->pimpl->init(typeInfo, nullptr, defaultPrecision, maximalDisplayString); + this->pimpl->init(typeInfo, nullptr, precision, maximalDisplayString); return; } @@ -229,27 +229,32 @@ void SmartLineEdit::setAmount(std::optional amount, std::optional p if (!amount) { // What the field is measuring doesn't matter as it's not set this->QLineEdit::setText(""); - } else if (std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)) { - // The field is not measuring a physical quantity so there are no units or unit conversions to handle + } else { + this->pimpl->m_precision = precision.value_or(this->pimpl->m_precision); - NonPhysicalQuantity const nonPhysicalQuantity = std::get(*this->pimpl->m_typeInfo->fieldType); - // It's a coding error if we're trying to pass a number in to a string field - Q_ASSERT(nonPhysicalQuantity != NonPhysicalQuantity::String); + if (std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)) { + // The field is not measuring a physical quantity so there are no units or unit conversions to handle - // For percentages, we'd like to show the % symbol after the number - QString symbol{""}; - if (NonPhysicalQuantity::Percentage == nonPhysicalQuantity) { - symbol = " %"; - } + NonPhysicalQuantity const nonPhysicalQuantity = + std::get(*this->pimpl->m_typeInfo->fieldType); + // It's a coding error if we're trying to pass a number in to a string field + Q_ASSERT(nonPhysicalQuantity != NonPhysicalQuantity::String); - this->QLineEdit::setText( - Measurement::displayQuantity(*amount, precision.value_or(this->pimpl->m_defaultPrecision)) + symbol - ); - } else { - // The field is measuring a physical quantity - this->QLineEdit::setText( - this->pimpl->m_uiAmountWithUnits->displayAmount(*amount, precision.value_or(this->pimpl->m_defaultPrecision)) - ); + // For percentages, we'd like to show the % symbol after the number + QString symbol{""}; + if (NonPhysicalQuantity::Percentage == nonPhysicalQuantity) { + symbol = " %"; + } + + this->QLineEdit::setText( + Measurement::displayQuantity(*amount, this->pimpl->m_precision) + symbol + ); + } else { + // The field is measuring a physical quantity + this->QLineEdit::setText( + this->pimpl->m_uiAmountWithUnits->displayAmount(*amount, this->pimpl->m_precision) + ); + } } this->pimpl->setDisplaySize(); @@ -289,9 +294,41 @@ QString SmartLineEdit::getForcedRelativeScaleViaString() const { Q_ASSERT( void SmartLineEdit::onLineChanged() { Q_ASSERT(this->pimpl->m_initialised); - if (std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)) { + // .:TODO:. Finish work on optional + if ("" == this->text() && this->pimpl->m_typeInfo->isOptional()) { + this->setAmount(std::nullopt); + } else if (std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)) { // The field is not measuring a physical quantity so there are no units or unit conversions to handle + // However, for anything other than a string, we still want to parse out the numeric part of the input qDebug() << Q_FUNC_INFO; + + // .:TBD:. At the moment, the special handling here for types other than double is a bit moot, but we keep it in + // case we need to do more in future. + NonPhysicalQuantity const nonPhysicalQuantity = + std::get(*this->pimpl->m_typeInfo->fieldType); + if (nonPhysicalQuantity != NonPhysicalQuantity::String) { + bool ok = false; + if (this->pimpl->m_typeInfo->typeIndex == typeid(double)) { + double amount = Measurement::extractRawFromString(this->text(), &ok); + this->setAmount(amount); + } else if (this->pimpl->m_typeInfo->typeIndex == typeid(int)) { + int amount = Measurement::extractRawFromString(this->text(), &ok); + this->setAmount(amount); + } else if (this->pimpl->m_typeInfo->typeIndex == typeid(uint)) { + uint amount = Measurement::extractRawFromString(this->text(), &ok); + this->setAmount(amount); + } else { + // It's a coding error if we get here + qCritical() << Q_FUNC_INFO << "Don't know how to parse" << this->pimpl->m_typeInfo; + Q_ASSERT(false); + } + if (!ok) { + qWarning() << + Q_FUNC_INFO << "Unable to extract number from" << this->text() << "for" << this->pimpl->m_typeInfo; + this->setAmount(0); + } + } + if (sender() == this) { emit textModified(); } @@ -337,7 +374,9 @@ void SmartLineEdit::lineChanged(PreviousScaleInfo previousScaleInfo) { return; } - this->QLineEdit::setText(this->pimpl->m_uiAmountWithUnits->correctEnteredText(this->text(), previousScaleInfo)); + this->QLineEdit::setText( + this->pimpl->m_uiAmountWithUnits->correctEnteredText(this->text(), this->pimpl->m_precision, previousScaleInfo) + ); if (sender() == this) { emit textModified(); diff --git a/src/widgets/SmartLineEdit.h b/src/widgets/SmartLineEdit.h index 48e0a3b1..ba8d0156 100644 --- a/src/widgets/SmartLineEdit.h +++ b/src/widgets/SmartLineEdit.h @@ -93,12 +93,12 @@ class SmartLineEdit : public QLineEdit { * canonical units if it is a \c PhysicalQuantity) and, whether this is an optional * field (in which case we need to handle blank / empty string as a valid value). * \param buddyLabel Required if \c fieldType is \b not a \c NonPhysicalQuantity - * \param defaultPrecision Where relevant determines the number of decimal places to show + * \param precision Where relevant determines the number of decimal places to show * \param maximalDisplayString Used for determining the width of the widget */ void init(TypeInfo const & typeInfo, SmartLabel & buddyLabel, - int const defaultPrecision = 3, + int const precision = 3, QString const & maximalDisplayString = "100.000 srm"); /** @@ -106,7 +106,7 @@ class SmartLineEdit : public QLineEdit { * \c NonPhysicalQuantity::String, etc. */ void init(TypeInfo const & typeInfo, - int const defaultPrecision = 3, + int const precision = 3, QString const & maximalDisplayString = "100.000 srm"); BtFieldType const getFieldType() const; diff --git a/ui/fermentableEditor.ui b/ui/fermentableEditor.ui index 41da0ee1..7b717264 100644 --- a/ui/fermentableEditor.ui +++ b/ui/fermentableEditor.ui @@ -150,7 +150,7 @@
- + 0 @@ -211,7 +211,7 @@ - + Qt::CustomContextMenu @@ -227,7 +227,7 @@ - + 0 @@ -249,7 +249,7 @@ - + Qt::CustomContextMenu @@ -265,7 +265,7 @@ - + 0 @@ -300,7 +300,7 @@ - + 0 @@ -329,7 +329,7 @@ - + 0 @@ -361,7 +361,7 @@ - + 0 @@ -393,7 +393,7 @@ - + 0 @@ -435,7 +435,7 @@ - + Qt::CustomContextMenu @@ -448,7 +448,7 @@ - + 0 @@ -493,7 +493,7 @@ - + 0 @@ -515,7 +515,7 @@ - + 0 @@ -550,7 +550,7 @@ - + 0 @@ -582,7 +582,7 @@ - + 0 @@ -700,66 +700,24 @@ - BtGenericEdit - QLineEdit -
BtLineEdit.h
-
- - BtStringEdit - QLineEdit -
BtLineEdit.h
-
- - BtColorLabel + SmartLabel QLabel -
BtLabel.h
+
widgets/SmartLabel.h
changedSystemOfMeasurementOrScale(PreviousScaleInfo) + popContextMenu(QPoint)
- BtColorEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtMassEdit + SmartLineEdit QLineEdit -
BtAmountEdit.h
+
widgets/SmartLineEdit.h
textModified() lineChanged() lineChanged(PreviousScaleInfo)
- - BtMassLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - popContextMenu(QPoint) - -
- - BtDiastaticPowerEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtDiastaticPowerLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
tabWidget_editor From 27a66c853c1e7328d2bea40eef7f5e80c20c7c04 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sat, 1 Apr 2023 03:14:14 +0200 Subject: [PATCH 12/42] Precision only needs to be set once --- src/FermentableEditor.cpp | 4 ++-- src/widgets/SmartLineEdit.cpp | 4 +--- src/widgets/SmartLineEdit.h | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/FermentableEditor.cpp b/src/FermentableEditor.cpp index 973428a2..713f4839 100644 --- a/src/FermentableEditor.cpp +++ b/src/FermentableEditor.cpp @@ -43,7 +43,7 @@ FermentableEditor::FermentableEditor(QWidget* parent) : } this->lineEdit_name ->init(Fermentable::typeLookup.getType(PropertyNames::NamedEntity::name ) ); - this->lineEdit_color ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::color_srm ), *this->label_color ); + this->lineEdit_color ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::color_srm ), *this->label_color , 0); this->lineEdit_diastaticPower->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::diastaticPower_lintner), *this->label_diastaticPower ); this->lineEdit_coarseFineDiff->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::coarseFineDiff_pct ) , 0); this->lineEdit_ibuGalPerLb ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::ibuGalPerLb ) , 0); @@ -145,7 +145,7 @@ void FermentableEditor::showChanges(QMetaProperty* metaProp) { lineEdit_name->setCursorPosition(0); tabWidget_editor->setTabText(0, obsFerm->name()); if (!updateAll) { return; } } if (updateAll || propName == PropertyNames::NamedEntityWithInventory::inventory) { lineEdit_inventory ->setAmount (obsFerm->inventory() ); if (!updateAll) { return; } } if (updateAll || propName == PropertyNames::Fermentable::yield_pct ) { lineEdit_yield ->setAmount (obsFerm->yield_pct() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::color_srm ) { lineEdit_color ->setAmount (obsFerm->color_srm(), 0 ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::color_srm ) { lineEdit_color ->setAmount (obsFerm->color_srm() ); if (!updateAll) { return; } } if (updateAll || propName == PropertyNames::Fermentable::addAfterBoil ) { checkBox_addAfterBoil ->setCheckState(obsFerm->addAfterBoil() ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } if (updateAll || propName == PropertyNames::Fermentable::origin ) { lineEdit_origin ->setText (obsFerm->origin() ); lineEdit_origin ->setCursorPosition(0); if (!updateAll) { return; } } if (updateAll || propName == PropertyNames::Fermentable::supplier ) { lineEdit_supplier ->setText (obsFerm->supplier() ); lineEdit_supplier->setCursorPosition(0); if (!updateAll) { return; } } diff --git a/src/widgets/SmartLineEdit.cpp b/src/widgets/SmartLineEdit.cpp index b1f8de60..b4c220f5 100644 --- a/src/widgets/SmartLineEdit.cpp +++ b/src/widgets/SmartLineEdit.cpp @@ -223,15 +223,13 @@ Measurement::Amount SmartLineEdit::toCanonical() const { return this->pimpl->m_uiAmountWithUnits->rawToCanonical(this->text()); } -void SmartLineEdit::setAmount(std::optional amount, std::optional precision) { +void SmartLineEdit::setAmount(std::optional amount) { Q_ASSERT(this->pimpl->m_initialised); if (!amount) { // What the field is measuring doesn't matter as it's not set this->QLineEdit::setText(""); } else { - this->pimpl->m_precision = precision.value_or(this->pimpl->m_precision); - if (std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)) { // The field is not measuring a physical quantity so there are no units or unit conversions to handle diff --git a/src/widgets/SmartLineEdit.h b/src/widgets/SmartLineEdit.h index ba8d0156..c332b8a1 100644 --- a/src/widgets/SmartLineEdit.h +++ b/src/widgets/SmartLineEdit.h @@ -130,9 +130,8 @@ class SmartLineEdit : public QLineEdit { * \brief Set the amount for a decimal field * * \param amount is the amount to display, but the field should be blank if this is \b std::nullopt - * \param precision is how many decimal places to show. If not specified, the default will be used. */ - void setAmount(std::optional amount, std::optional precision = std::nullopt); + void setAmount(std::optional amount); // /** // * \brief Set the text for a non-numeric field From f35a83cb7ca0433e395b06b06c1f318c7748f80f Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sun, 2 Apr 2023 10:48:46 +0200 Subject: [PATCH 13/42] Add SmartLineEdit etc to HopEditor --- src/BrewNoteWidget.cpp | 31 ++++-- src/EquipmentEditor.cpp | 47 ++++++--- src/FermentableEditor.cpp | 37 +++++--- src/HopEditor.cpp | 126 +++++++++++++++--------- src/MiscEditor.cpp | 10 +- src/model/Hop.cpp | 82 ++++++++-------- src/model/Hop.h | 96 +++++++++---------- src/widgets/SmartLineEdit.cpp | 174 +++++++++++++++++++++++----------- src/widgets/SmartLineEdit.h | 65 +++++++++++-- ui/hopEditor.ui | 87 ++++++----------- 10 files changed, 462 insertions(+), 293 deletions(-) diff --git a/src/BrewNoteWidget.cpp b/src/BrewNoteWidget.cpp index 2dea52fc..fabb3230 100644 --- a/src/BrewNoteWidget.cpp +++ b/src/BrewNoteWidget.cpp @@ -38,16 +38,27 @@ BrewNoteWidget::BrewNoteWidget(QWidget *parent) : QWidget(parent) { bNoteObs = 0; setObjectName("BrewNoteWidget"); - this->lineEdit_Fg ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::fg ), *this->label_Fg ); - this->lineEdit_Og ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::og ), *this->label_Og ); - this->lineEdit_Sg ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::sg ), *this->label_Sg ); - this->lineEdit_mashFinTemp->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::mashFinTemp_c ), *this->label_mashFinTemp); - this->lineEdit_pitchTemp ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::pitchTemp_c ), *this->label_pitchTemp ); - this->lineEdit_strikeTemp ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::strikeTemp_c ), *this->label_strikeTemp ); - this->lineEdit_finalVolume->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::finalVolume_l ), *this->label_finalVolume ); - this->lineEdit_postBoilVol->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::postBoilVolume_l), *this->label_postBoilVol); - this->lineEdit_volIntoBk ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::volumeIntoBK_l ), *this->label_volIntoBk ); - this->lineEdit_volIntoFerm->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::volumeIntoFerm_l), *this->label_volIntoFerm); + SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_Fg , PropertyNames::BrewNote::fg , *this->label_Fg ); + SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_Og , PropertyNames::BrewNote::og , *this->label_Og ); + SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_Sg , PropertyNames::BrewNote::sg , *this->label_Sg ); + SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_mashFinTemp, PropertyNames::BrewNote::mashFinTemp_c , *this->label_mashFinTemp); + SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_pitchTemp , PropertyNames::BrewNote::pitchTemp_c , *this->label_pitchTemp ); + SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_strikeTemp , PropertyNames::BrewNote::strikeTemp_c , *this->label_strikeTemp ); + SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_finalVolume, PropertyNames::BrewNote::finalVolume_l , *this->label_finalVolume); + SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_postBoilVol, PropertyNames::BrewNote::postBoilVolume_l, *this->label_postBoilVol); + SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_volIntoBk , PropertyNames::BrewNote::volumeIntoBK_l , *this->label_volIntoBk ); + SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_volIntoFerm, PropertyNames::BrewNote::volumeIntoFerm_l, *this->label_volIntoFerm); + +/// this->lineEdit_Fg ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::fg ), *this->label_Fg ); +/// this->lineEdit_Og ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::og ), *this->label_Og ); +/// this->lineEdit_Sg ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::sg ), *this->label_Sg ); +/// this->lineEdit_mashFinTemp->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::mashFinTemp_c ), *this->label_mashFinTemp); +/// this->lineEdit_pitchTemp ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::pitchTemp_c ), *this->label_pitchTemp ); +/// this->lineEdit_strikeTemp ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::strikeTemp_c ), *this->label_strikeTemp ); +/// this->lineEdit_finalVolume->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::finalVolume_l ), *this->label_finalVolume ); +/// this->lineEdit_postBoilVol->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::postBoilVolume_l), *this->label_postBoilVol); +/// this->lineEdit_volIntoBk ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::volumeIntoBK_l ), *this->label_volIntoBk ); +/// this->lineEdit_volIntoFerm->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::volumeIntoFerm_l), *this->label_volIntoFerm); connect(this->lineEdit_Sg, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateSG ); connect(this->lineEdit_volIntoBk, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateVolumeIntoBK_l ); diff --git a/src/EquipmentEditor.cpp b/src/EquipmentEditor.cpp index c2074af6..323cce1d 100644 --- a/src/EquipmentEditor.cpp +++ b/src/EquipmentEditor.cpp @@ -72,21 +72,38 @@ EquipmentEditor::EquipmentEditor(QWidget* parent, bool singleEquipEditor) : obsEquip = nullptr; - this->lineEdit_tunSpecificHeat->init(Equipment::typeLookup.getType(PropertyNames::Equipment::tunSpecificHeat_calGC), *this->label_tunSpecificHeat ); - this->lineEdit_grainAbsorption->init(Equipment::typeLookup.getType(PropertyNames::Equipment::grainAbsorption_LKg ) ); - this->lineEdit_hopUtilization ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::hopUtilization_pct ) , 0); // label_hopUtilization - this->lineEdit_tunWeight ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::tunWeight_kg ), *this->label_tunWeight ); - this->lineEdit_name ->init(Equipment::typeLookup.getType(PropertyNames::NamedEntity::name ) ); - this->lineEdit_boilingPoint ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::boilingPoint_c ), *this->label_boilingPoint , 1); - this->lineEdit_boilTime ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::boilTime_min ), *this->label_boilTime ); - this->lineEdit_batchSize ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::batchSize_l ), *this->label_batchSize ); - this->lineEdit_boilSize ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::boilSize_l ), *this->label_boilSize ); - this->lineEdit_evaporationRate->init(Equipment::typeLookup.getType(PropertyNames::Equipment::evapRate_lHr ), *this->label_evaporationRate ); - this->lineEdit_lauterDeadspace->init(Equipment::typeLookup.getType(PropertyNames::Equipment::lauterDeadspace_l ), *this->label_lauterDeadspace ); - this->lineEdit_topUpKettle ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::topUpKettle_l ), *this->label_topUpKettle ); - this->lineEdit_topUpWater ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::topUpWater_l ), *this->label_topUpWater ); - this->lineEdit_trubChillerLoss->init(Equipment::typeLookup.getType(PropertyNames::Equipment::trubChillerLoss_l ), *this->label_trubChillerLoss ); - this->lineEdit_tunVolume ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::tunVolume_l ), *this->label_tunVolume ); + SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_tunSpecificHeat, PropertyNames::Equipment::tunSpecificHeat_calGC, *this->label_tunSpecificHeat ); + SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_grainAbsorption, PropertyNames::Equipment::grainAbsorption_LKg ); + SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_hopUtilization , PropertyNames::Equipment::hopUtilization_pct , 0); + SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_tunWeight , PropertyNames::Equipment::tunWeight_kg , *this->label_tunWeight ); + SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_name , PropertyNames::NamedEntity::name ); + SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_boilingPoint , PropertyNames::Equipment::boilingPoint_c , *this->label_boilingPoint , 1); + SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_boilTime , PropertyNames::Equipment::boilTime_min , *this->label_boilTime ); + SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_batchSize , PropertyNames::Equipment::batchSize_l , *this->label_batchSize ); + SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_boilSize , PropertyNames::Equipment::boilSize_l , *this->label_boilSize ); + SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_evaporationRate, PropertyNames::Equipment::evapRate_lHr , *this->label_evaporationRate ); + SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_lauterDeadspace, PropertyNames::Equipment::lauterDeadspace_l , *this->label_lauterDeadspace ); + SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_topUpKettle , PropertyNames::Equipment::topUpKettle_l , *this->label_topUpKettle ); + SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_topUpWater , PropertyNames::Equipment::topUpWater_l , *this->label_topUpWater ); + SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_trubChillerLoss, PropertyNames::Equipment::trubChillerLoss_l , *this->label_trubChillerLoss ); + SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_tunVolume , PropertyNames::Equipment::tunVolume_l , *this->label_tunVolume ); + + +/// this->lineEdit_tunSpecificHeat->init(Equipment::typeLookup.getType(PropertyNames::Equipment::tunSpecificHeat_calGC), *this->label_tunSpecificHeat ); +/// this->lineEdit_grainAbsorption->init(Equipment::typeLookup.getType(PropertyNames::Equipment::grainAbsorption_LKg ) ); +/// this->lineEdit_hopUtilization ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::hopUtilization_pct ) , 0); // label_hopUtilization +/// this->lineEdit_tunWeight ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::tunWeight_kg ), *this->label_tunWeight ); +/// this->lineEdit_name ->init(Equipment::typeLookup.getType(PropertyNames::NamedEntity::name ) ); +/// this->lineEdit_boilingPoint ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::boilingPoint_c ), *this->label_boilingPoint , 1); +/// this->lineEdit_boilTime ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::boilTime_min ), *this->label_boilTime ); +/// this->lineEdit_batchSize ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::batchSize_l ), *this->label_batchSize ); +/// this->lineEdit_boilSize ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::boilSize_l ), *this->label_boilSize ); +/// this->lineEdit_evaporationRate->init(Equipment::typeLookup.getType(PropertyNames::Equipment::evapRate_lHr ), *this->label_evaporationRate ); +/// this->lineEdit_lauterDeadspace->init(Equipment::typeLookup.getType(PropertyNames::Equipment::lauterDeadspace_l ), *this->label_lauterDeadspace ); +/// this->lineEdit_topUpKettle ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::topUpKettle_l ), *this->label_topUpKettle ); +/// this->lineEdit_topUpWater ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::topUpWater_l ), *this->label_topUpWater ); +/// this->lineEdit_trubChillerLoss->init(Equipment::typeLookup.getType(PropertyNames::Equipment::trubChillerLoss_l ), *this->label_trubChillerLoss ); +/// this->lineEdit_tunVolume ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::tunVolume_l ), *this->label_tunVolume ); // Connect all the edit boxen connect(lineEdit_boilTime, &SmartLineEdit::textModified, this, &EquipmentEditor::updateCheckboxRecord ); diff --git a/src/FermentableEditor.cpp b/src/FermentableEditor.cpp index 713f4839..a8137125 100644 --- a/src/FermentableEditor.cpp +++ b/src/FermentableEditor.cpp @@ -42,18 +42,31 @@ FermentableEditor::FermentableEditor(QWidget* parent) : Fermentable::typeStringMapping.enumToString(ii)); } - this->lineEdit_name ->init(Fermentable::typeLookup.getType(PropertyNames::NamedEntity::name ) ); - this->lineEdit_color ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::color_srm ), *this->label_color , 0); - this->lineEdit_diastaticPower->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::diastaticPower_lintner), *this->label_diastaticPower ); - this->lineEdit_coarseFineDiff->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::coarseFineDiff_pct ) , 0); - this->lineEdit_ibuGalPerLb ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::ibuGalPerLb ) , 0); - this->lineEdit_maxInBatch ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::maxInBatch_pct ) , 0); - this->lineEdit_moisture ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::moisture_pct ) , 0); - this->lineEdit_protein ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::protein_pct ) , 0); - this->lineEdit_yield ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::yield_pct ) , 1); - this->lineEdit_inventory ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::amount ), *this->label_inventory ); - this->lineEdit_origin ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::origin ) ); - this->lineEdit_supplier ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::supplier ) ); + SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_name , PropertyNames::NamedEntity::name ); + SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_color , PropertyNames::Fermentable::color_srm , *this->label_color , 0); + SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_diastaticPower, PropertyNames::Fermentable::diastaticPower_lintner, *this->label_diastaticPower ); + SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_coarseFineDiff, PropertyNames::Fermentable::coarseFineDiff_pct , 0); + SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_ibuGalPerLb , PropertyNames::Fermentable::ibuGalPerLb , 0); + SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_maxInBatch , PropertyNames::Fermentable::maxInBatch_pct , 0); + SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_moisture , PropertyNames::Fermentable::moisture_pct , 0); + SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_protein , PropertyNames::Fermentable::protein_pct , 0); + SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_yield , PropertyNames::Fermentable::yield_pct , 1); + SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_inventory , PropertyNames::Fermentable::amount , *this->label_inventory ); + SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_origin , PropertyNames::Fermentable::origin ); + SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_supplier , PropertyNames::Fermentable::supplier ); + +// this->lineEdit_name ->init(Fermentable::typeLookup.getType(PropertyNames::NamedEntity::name ) ); +// this->lineEdit_color ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::color_srm ), *this->label_color , 0); +// this->lineEdit_diastaticPower->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::diastaticPower_lintner), *this->label_diastaticPower ); +// this->lineEdit_coarseFineDiff->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::coarseFineDiff_pct ) , 0); +// this->lineEdit_ibuGalPerLb ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::ibuGalPerLb ) , 0); +// this->lineEdit_maxInBatch ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::maxInBatch_pct ) , 0); +// this->lineEdit_moisture ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::moisture_pct ) , 0); +// this->lineEdit_protein ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::protein_pct ) , 0); +// this->lineEdit_yield ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::yield_pct ) , 1); +// this->lineEdit_inventory ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::amount ), *this->label_inventory ); +// this->lineEdit_origin ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::origin ) ); +// this->lineEdit_supplier ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::supplier ) ); connect(pushButton_new, &QAbstractButton::clicked, this, &FermentableEditor::clickedNewFermentable); connect(pushButton_save, &QAbstractButton::clicked, this, &FermentableEditor::save); diff --git a/src/HopEditor.cpp b/src/HopEditor.cpp index 9ef92a82..1e752d78 100644 --- a/src/HopEditor.cpp +++ b/src/HopEditor.cpp @@ -37,6 +37,32 @@ HopEditor::HopEditor(QWidget * parent) : this->tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_name , PropertyNames::NamedEntity::name ); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_alpha , PropertyNames::Hop::alpha_pct , 0); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_inventory , PropertyNames::Hop::amount_kg , *this->label_inventory ); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_time , PropertyNames::Hop::time_min , *this->label_time , 0); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_beta , PropertyNames::Hop::beta_pct , 0); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_HSI , PropertyNames::Hop::hsi_pct , 0); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_origin , PropertyNames::Hop::origin ); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_humulene , PropertyNames::Hop::humulene_pct , 0); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_caryophyllene , PropertyNames::Hop::caryophyllene_pct , 0); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_cohumulone , PropertyNames::Hop::cohumulone_pct , 0); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_myrcene , PropertyNames::Hop::myrcene_pct , 0); + // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_producer , PropertyNames::Hop::producer ); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_product_id , PropertyNames::Hop::product_id ); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_year , PropertyNames::Hop::year ); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_total_oil_ml_per_100g, PropertyNames::Hop::total_oil_ml_per_100g ); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_farnesene , PropertyNames::Hop::farnesene_pct , 0); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_geraniol , PropertyNames::Hop::geraniol_pct , 0); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_b_pinene , PropertyNames::Hop::b_pinene_pct , 0); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_linalool , PropertyNames::Hop::linalool_pct , 0); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_limonene , PropertyNames::Hop::limonene_pct , 0); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_nerol , PropertyNames::Hop::nerol_pct , 0); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_pinene , PropertyNames::Hop::pinene_pct , 0); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_polyphenols , PropertyNames::Hop::polyphenols_pct , 0); + SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_xanthohumol , PropertyNames::Hop::xanthohumol_pct , 0); + // // According to https://bugreports.qt.io/browse/QTBUG-50823 it is never going to be possible to specify the data (as // opposed to display text) for a combo box via the .ui file. So we have to do it in code instead. @@ -53,9 +79,9 @@ HopEditor::HopEditor(QWidget * parent) : this->comboBox_hopUse->addItem (Hop::useDisplayNames[ii], Hop::useStringMapping.enumToString(ii)); } - connect(pushButton_new, &QAbstractButton::clicked, this, &HopEditor::clickedNewHop); - connect(pushButton_save, &QAbstractButton::clicked, this, &HopEditor::save); - connect(pushButton_cancel, &QAbstractButton::clicked, this, &HopEditor::clearAndClose); + connect(this->pushButton_new, &QAbstractButton::clicked, this, &HopEditor::clickedNewHop); + connect(this->pushButton_save, &QAbstractButton::clicked, this, &HopEditor::save); + connect(this->pushButton_cancel, &QAbstractButton::clicked, this, &HopEditor::clearAndClose); return; } @@ -81,18 +107,18 @@ void HopEditor::save() { return; } - this->obsHop->setName (lineEdit_name ->text()); - this->obsHop->setAlpha_pct (lineEdit_alpha ->getValueAs()); - this->obsHop->setTime_min (lineEdit_time ->toCanonical().quantity()); - this->obsHop->setBeta_pct (lineEdit_beta ->getValueAs()); - this->obsHop->setHsi_pct (lineEdit_HSI ->getValueAs()); - this->obsHop->setOrigin (lineEdit_origin ->text() ); - this->obsHop->setHumulene_pct (lineEdit_humulene ->getValueAs()); - this->obsHop->setCaryophyllene_pct(lineEdit_caryophyllene->getValueAs()); - this->obsHop->setCohumulone_pct (lineEdit_cohumulone ->getValueAs()); - this->obsHop->setMyrcene_pct (lineEdit_myrcene ->getValueAs()); - this->obsHop->setSubstitutes (textEdit_substitutes ->toPlainText() ); - this->obsHop->setNotes (textEdit_notes ->toPlainText() ); + this->obsHop->setName (this->lineEdit_name ->text ()); + this->obsHop->setAlpha_pct (this->lineEdit_alpha ->getValueAs ()); + this->obsHop->setTime_min (this->lineEdit_time ->toCanonical().quantity()); + this->obsHop->setBeta_pct (this->lineEdit_beta ->getValueAs ()); + this->obsHop->setHsi_pct (this->lineEdit_HSI ->getValueAs ()); + this->obsHop->setOrigin (this->lineEdit_origin ->text ()); + this->obsHop->setHumulene_pct (this->lineEdit_humulene ->getValueAs ()); + this->obsHop->setCaryophyllene_pct(this->lineEdit_caryophyllene->getValueAs ()); + this->obsHop->setCohumulone_pct (this->lineEdit_cohumulone ->getValueAs ()); + this->obsHop->setMyrcene_pct (this->lineEdit_myrcene ->getValueAs ()); + this->obsHop->setSubstitutes (this->textEdit_substitutes ->toPlainText ()); + this->obsHop->setNotes (this->textEdit_notes ->toPlainText ()); // // It's a coding error if we don't recognise the values in our own combo boxes, so it's OK that we'd get a @@ -103,7 +129,19 @@ void HopEditor::save() { this->obsHop->setUse (Hop::useStringMapping.stringToEnum (comboBox_hopUse->currentData().toString())); // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - + this->obsHop->setProducer (this->lineEdit_producer ->text ()); + this->obsHop->setProduct_id (this->lineEdit_product_id ->text ()); + this->obsHop->setYear (this->lineEdit_year ->getValueAs ()); + this->obsHop->setTotal_oil_ml_per_100g(this->lineEdit_total_oil_ml_per_100g->getValueAs ()); + this->obsHop->setFarnesene_pct (this->lineEdit_farnesene ->getValueAs ()); + this->obsHop->setGeraniol_pct (this->lineEdit_geraniol ->getValueAs ()); + this->obsHop->setB_pinene_pct (this->lineEdit_b_pinene ->getValueAs ()); + this->obsHop->setLinalool_pct (this->lineEdit_linalool ->getValueAs ()); + this->obsHop->setLimonene_pct (this->lineEdit_limonene ->getValueAs ()); + this->obsHop->setNerol_pct (this->lineEdit_nerol ->getValueAs ()); + this->obsHop->setPinene_pct (this->lineEdit_pinene ->getValueAs ()); + this->obsHop->setPolyphenols_pct (this->lineEdit_polyphenols ->getValueAs ()); + this->obsHop->setXanthohumol_pct (this->lineEdit_xanthohumol ->getValueAs ()); if (this->obsHop->key() < 0) { ObjectStoreWrapper::insert(*this->obsHop); @@ -167,34 +205,36 @@ void HopEditor::showChanges(QMetaProperty * prop) { return; } } - if (updateAll || propName == PropertyNames::NamedEntity::name ) { lineEdit_name ->setText(obsHop->name ()); // Continues to next line - lineEdit_name ->setCursorPosition(0); tabWidget_editor->setTabText(0, obsHop->name()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::origin ) { lineEdit_origin ->setText(obsHop->origin ()); lineEdit_origin->setCursorPosition(0); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::alpha_pct ) { lineEdit_alpha ->setText(obsHop->alpha_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::time_min ) { lineEdit_time ->setText(obsHop->time_min ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::beta_pct ) { lineEdit_beta ->setText(obsHop->beta_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::hsi_pct ) { lineEdit_HSI ->setText(obsHop->hsi_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::humulene_pct ) { lineEdit_humulene ->setText(obsHop->humulene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::caryophyllene_pct ) { lineEdit_caryophyllene ->setText(obsHop->caryophyllene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::cohumulone_pct ) { lineEdit_cohumulone ->setText(obsHop->cohumulone_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::myrcene_pct ) { lineEdit_myrcene ->setText(obsHop->myrcene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::substitutes ) { textEdit_substitutes ->setPlainText(obsHop->substitutes ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::notes ) { textEdit_notes ->setPlainText(obsHop->notes ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::NamedEntityWithInventory::inventory) { lineEdit_inventory ->setText(obsHop->inventory ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::NamedEntity::name ) { this->lineEdit_name ->setText (obsHop->name ()); // Continues to next line + this->lineEdit_name ->setCursorPosition(0); // Continues to next line + this->tabWidget_editor ->setTabText(0, obsHop->name()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::origin ) { this->lineEdit_origin ->setText (obsHop->origin ()); // Continues to next line + this->lineEdit_origin ->setCursorPosition(0); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::alpha_pct ) { this->lineEdit_alpha ->setAmount (obsHop->alpha_pct ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::time_min ) { this->lineEdit_time ->setAmount (obsHop->time_min ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::beta_pct ) { this->lineEdit_beta ->setAmount (obsHop->beta_pct ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::hsi_pct ) { this->lineEdit_HSI ->setAmount (obsHop->hsi_pct ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::humulene_pct ) { this->lineEdit_humulene ->setAmount (obsHop->humulene_pct ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::caryophyllene_pct ) { this->lineEdit_caryophyllene ->setAmount (obsHop->caryophyllene_pct ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::cohumulone_pct ) { this->lineEdit_cohumulone ->setAmount (obsHop->cohumulone_pct ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::myrcene_pct ) { this->lineEdit_myrcene ->setAmount (obsHop->myrcene_pct ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::substitutes ) { this->textEdit_substitutes ->setPlainText(obsHop->substitutes ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::notes ) { this->textEdit_notes ->setPlainText(obsHop->notes ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::NamedEntityWithInventory::inventory) { this->lineEdit_inventory ->setAmount (obsHop->inventory ()); if (!updateAll) { return; } } // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - if (updateAll || propName == PropertyNames::Hop::producer ) {lineEdit_producer ->setText(obsHop->producer ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::product_id ) {lineEdit_product_id ->setText(obsHop->product_id ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::year ) {lineEdit_year ->setText(obsHop->year ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::total_oil_ml_per_100g ) {lineEdit_total_oil_ml_per_100g->setText(obsHop->total_oil_ml_per_100g()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::farnesene_pct ) {lineEdit_farnesene ->setText(obsHop->farnesene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::geraniol_pct ) {lineEdit_geraniol ->setText(obsHop->geraniol_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::b_pinene_pct ) {lineEdit_b_pinene ->setText(obsHop->b_pinene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::linalool_pct ) {lineEdit_linalool ->setText(obsHop->linalool_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::limonene_pct ) {lineEdit_limonene ->setText(obsHop->limonene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::nerol_pct ) {lineEdit_nerol ->setText(obsHop->nerol_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::pinene_pct ) {lineEdit_pinene ->setText(obsHop->pinene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::polyphenols_pct ) {lineEdit_polyphenols ->setText(obsHop->polyphenols_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::xanthohumol_pct ) {lineEdit_xanthohumol ->setText(obsHop->xanthohumol_pct ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::producer ) { this->lineEdit_producer ->setText (obsHop->producer ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::product_id ) { this->lineEdit_product_id ->setText (obsHop->product_id ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::year ) { this->lineEdit_year ->setAmount (obsHop->year ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::total_oil_ml_per_100g ) { this->lineEdit_total_oil_ml_per_100g->setAmount (obsHop->total_oil_ml_per_100g()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::farnesene_pct ) { this->lineEdit_farnesene ->setAmount (obsHop->farnesene_pct ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::geraniol_pct ) { this->lineEdit_geraniol ->setAmount (obsHop->geraniol_pct ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::b_pinene_pct ) { this->lineEdit_b_pinene ->setAmount (obsHop->b_pinene_pct ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::linalool_pct ) { this->lineEdit_linalool ->setAmount (obsHop->linalool_pct ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::limonene_pct ) { this->lineEdit_limonene ->setAmount (obsHop->limonene_pct ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::nerol_pct ) { this->lineEdit_nerol ->setAmount (obsHop->nerol_pct ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::pinene_pct ) { this->lineEdit_pinene ->setAmount (obsHop->pinene_pct ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::polyphenols_pct ) { this->lineEdit_polyphenols ->setAmount (obsHop->polyphenols_pct ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Hop::xanthohumol_pct ) { this->lineEdit_xanthohumol ->setAmount (obsHop->xanthohumol_pct ()); if (!updateAll) { return; } } return; } diff --git a/src/MiscEditor.cpp b/src/MiscEditor.cpp index 7cdea216..26a657f7 100644 --- a/src/MiscEditor.cpp +++ b/src/MiscEditor.cpp @@ -36,9 +36,13 @@ MiscEditor::MiscEditor(QWidget * parent) : tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs); - this->lineEdit_name ->init(Misc::typeLookup.getType(PropertyNames::NamedEntity::name) ); - this->lineEdit_inventory->init(Misc::typeLookup.getType(PropertyNames::Misc::amount ), *this->label_inventory); - this->lineEdit_time ->init(Misc::typeLookup.getType(PropertyNames::Misc::time ), *this->label_time ); + SMART_LINE_EDIT_INIT(MiscEditor, Misc, lineEdit_name , PropertyNames::NamedEntity::name ); + SMART_LINE_EDIT_INIT(MiscEditor, Misc, lineEdit_inventory, PropertyNames::Misc::amount , *this->label_inventory); + SMART_LINE_EDIT_INIT(MiscEditor, Misc, lineEdit_time , PropertyNames::Misc::time , *this->label_time ); + +/// this->lineEdit_name ->init(Misc::typeLookup.getType(PropertyNames::NamedEntity::name) ); +/// this->lineEdit_inventory->init(Misc::typeLookup.getType(PropertyNames::Misc::amount ), *this->label_inventory); +/// this->lineEdit_time ->init(Misc::typeLookup.getType(PropertyNames::Misc::time ), *this->label_time ); // Note, per https://wiki.qt.io/New_Signal_Slot_Syntax#Default_arguments_in_slot, the use of a trivial lambda // function to allow use of default argument on newStyle() slot diff --git a/src/model/Hop.cpp b/src/model/Hop.cpp index 917e794d..79f7f694 100644 --- a/src/model/Hop.cpp +++ b/src/model/Hop.cpp @@ -307,54 +307,54 @@ double Hop::caryophyllene_pct() const { return this->m_caryop double Hop::cohumulone_pct() const { return this->m_cohumulone_pct; } double Hop::myrcene_pct() const { return this->m_myrcene_pct; } // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ -QString Hop::producer() const { return this->m_producer; } -QString Hop::product_id() const { return this->m_product_id; } -std::optional Hop::year() const { return this->m_year; } -std::optional Hop::total_oil_ml_per_100g() const { return this->m_total_oil_ml_per_100g; } -std::optional Hop::farnesene_pct() const { return this->m_farnesene_pct; } -std::optional Hop::geraniol_pct() const { return this->m_geraniol_pct; } -std::optional Hop::b_pinene_pct() const { return this->m_b_pinene_pct; } -std::optional Hop::linalool_pct() const { return this->m_linalool_pct; } -std::optional Hop::limonene_pct() const { return this->m_limonene_pct; } -std::optional Hop::nerol_pct() const { return this->m_nerol_pct; } -std::optional Hop::pinene_pct() const { return this->m_pinene_pct; } -std::optional Hop::polyphenols_pct() const { return this->m_polyphenols_pct; } -std::optional Hop::xanthohumol_pct() const { return this->m_xanthohumol_pct; } +QString Hop::producer() const { return this->m_producer; } +QString Hop::product_id() const { return this->m_product_id; } +std::optional Hop::year() const { return this->m_year; } +std::optional Hop::total_oil_ml_per_100g() const { return this->m_total_oil_ml_per_100g; } +std::optional Hop::farnesene_pct() const { return this->m_farnesene_pct; } +std::optional Hop::geraniol_pct() const { return this->m_geraniol_pct; } +std::optional Hop::b_pinene_pct() const { return this->m_b_pinene_pct; } +std::optional Hop::linalool_pct() const { return this->m_linalool_pct; } +std::optional Hop::limonene_pct() const { return this->m_limonene_pct; } +std::optional Hop::nerol_pct() const { return this->m_nerol_pct; } +std::optional Hop::pinene_pct() const { return this->m_pinene_pct; } +std::optional Hop::polyphenols_pct() const { return this->m_polyphenols_pct; } +std::optional Hop::xanthohumol_pct() const { return this->m_xanthohumol_pct; } double Hop::inventory() const { return InventoryUtils::getAmount(*this); } //============================================= "SETTER" MEMBER FUNCTIONS ============================================== -void Hop::setAlpha_pct (double const val) { this->setAndNotify(PropertyNames::Hop::alpha_pct, this->m_alpha_pct, this->enforceMinAndMax(val, "alpha", 0.0, 100.0)); } -void Hop::setAmount_kg (double const val) { this->setAndNotify(PropertyNames::Hop::amount_kg, this->m_amount_kg, this->enforceMin (val, "amount") ); } -void Hop::setUse (Hop::Use const val) { this->setAndNotify(PropertyNames::Hop::use, this->m_use, val ); } -void Hop::setTime_min (double const val) { this->setAndNotify(PropertyNames::Hop::time_min, this->m_time_min, this->enforceMin (val, "time") ); } -void Hop::setNotes (QString const & val) { this->setAndNotify(PropertyNames::Hop::notes, this->m_notes, val ); } -void Hop::setType (Hop::Type const val) { this->setAndNotify(PropertyNames::Hop::type, this->m_type, val ); } -void Hop::setForm (Hop::Form const val) { this->setAndNotify(PropertyNames::Hop::form, this->m_form, val ); } -void Hop::setBeta_pct (double const val) { this->setAndNotify(PropertyNames::Hop::beta_pct, this->m_beta_pct, this->enforceMinAndMax(val, "beta", 0.0, 100.0)); } -void Hop::setHsi_pct (double const val) { this->setAndNotify(PropertyNames::Hop::hsi_pct, this->m_hsi_pct, this->enforceMinAndMax(val, "hsi", 0.0, 100.0)); } -void Hop::setOrigin (QString const & val) { this->setAndNotify(PropertyNames::Hop::origin, this->m_origin, val ); } -void Hop::setSubstitutes (QString const & val) { this->setAndNotify(PropertyNames::Hop::substitutes, this->m_substitutes, val ); } -void Hop::setHumulene_pct (double const val) { this->setAndNotify(PropertyNames::Hop::humulene_pct, this->m_humulene_pct, this->enforceMinAndMax(val, "humulene", 0.0, 100.0)); } -void Hop::setCaryophyllene_pct (double const val) { this->setAndNotify(PropertyNames::Hop::caryophyllene_pct, this->m_caryophyllene_pct, this->enforceMinAndMax(val, "caryophyllene", 0.0, 100.0)); } -void Hop::setCohumulone_pct (double const val) { this->setAndNotify(PropertyNames::Hop::cohumulone_pct, this->m_cohumulone_pct, this->enforceMinAndMax(val, "cohumulone", 0.0, 100.0)); } -void Hop::setMyrcene_pct (double const val) { this->setAndNotify(PropertyNames::Hop::myrcene_pct, this->m_myrcene_pct, this->enforceMinAndMax(val, "myrcene", 0.0, 100.0)); } +void Hop::setAlpha_pct (double const val) { this->setAndNotify(PropertyNames::Hop::alpha_pct, this->m_alpha_pct, this->enforceMinAndMax(val, "alpha", 0.0, 100.0)); } +void Hop::setAmount_kg (double const val) { this->setAndNotify(PropertyNames::Hop::amount_kg, this->m_amount_kg, this->enforceMin (val, "amount") ); } +void Hop::setUse (Hop::Use const val) { this->setAndNotify(PropertyNames::Hop::use, this->m_use, val ); } +void Hop::setTime_min (double const val) { this->setAndNotify(PropertyNames::Hop::time_min, this->m_time_min, this->enforceMin (val, "time") ); } +void Hop::setNotes (QString const & val) { this->setAndNotify(PropertyNames::Hop::notes, this->m_notes, val ); } +void Hop::setType (Hop::Type const val) { this->setAndNotify(PropertyNames::Hop::type, this->m_type, val ); } +void Hop::setForm (Hop::Form const val) { this->setAndNotify(PropertyNames::Hop::form, this->m_form, val ); } +void Hop::setBeta_pct (double const val) { this->setAndNotify(PropertyNames::Hop::beta_pct, this->m_beta_pct, this->enforceMinAndMax(val, "beta", 0.0, 100.0)); } +void Hop::setHsi_pct (double const val) { this->setAndNotify(PropertyNames::Hop::hsi_pct, this->m_hsi_pct, this->enforceMinAndMax(val, "hsi", 0.0, 100.0)); } +void Hop::setOrigin (QString const & val) { this->setAndNotify(PropertyNames::Hop::origin, this->m_origin, val ); } +void Hop::setSubstitutes (QString const & val) { this->setAndNotify(PropertyNames::Hop::substitutes, this->m_substitutes, val ); } +void Hop::setHumulene_pct (double const val) { this->setAndNotify(PropertyNames::Hop::humulene_pct, this->m_humulene_pct, this->enforceMinAndMax(val, "humulene", 0.0, 100.0)); } +void Hop::setCaryophyllene_pct (double const val) { this->setAndNotify(PropertyNames::Hop::caryophyllene_pct, this->m_caryophyllene_pct, this->enforceMinAndMax(val, "caryophyllene", 0.0, 100.0)); } +void Hop::setCohumulone_pct (double const val) { this->setAndNotify(PropertyNames::Hop::cohumulone_pct, this->m_cohumulone_pct, this->enforceMinAndMax(val, "cohumulone", 0.0, 100.0)); } +void Hop::setMyrcene_pct (double const val) { this->setAndNotify(PropertyNames::Hop::myrcene_pct, this->m_myrcene_pct, this->enforceMinAndMax(val, "myrcene", 0.0, 100.0)); } // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ -void Hop::setProducer (QString const & val) { this->setAndNotify(PropertyNames::Hop::producer, this->m_producer, val ); } -void Hop::setProduct_id (QString const & val) { this->setAndNotify(PropertyNames::Hop::product_id, this->m_product_id, val ); } -void Hop::setYear (std::optional const val) { this->setAndNotify(PropertyNames::Hop::year, this->m_year, val ); } -void Hop::setTotal_oil_ml_per_100g(std::optional const val) { this->setAndNotify(PropertyNames::Hop::total_oil_ml_per_100g, this->m_total_oil_ml_per_100g, this->enforceMinAndMax(val, "total_oil_ml_per_100g", 0.0, 100.0)); } -void Hop::setFarnesene_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::farnesene_pct, this->m_farnesene_pct, this->enforceMinAndMax(val, "farnesene_pct", 0.0, 100.0)); } -void Hop::setGeraniol_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::geraniol_pct, this->m_geraniol_pct, this->enforceMinAndMax(val, "geraniol_pct", 0.0, 100.0)); } -void Hop::setB_pinene_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::b_pinene_pct, this->m_b_pinene_pct, this->enforceMinAndMax(val, "b_pinene_pct", 0.0, 100.0)); } -void Hop::setLinalool_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::linalool_pct, this->m_linalool_pct, this->enforceMinAndMax(val, "linalool_pct", 0.0, 100.0)); } -void Hop::setLimonene_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::limonene_pct, this->m_limonene_pct, this->enforceMinAndMax(val, "limonene_pct", 0.0, 100.0)); } -void Hop::setNerol_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::nerol_pct, this->m_nerol_pct, this->enforceMinAndMax(val, "nerol_pct", 0.0, 100.0)); } -void Hop::setPinene_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::pinene_pct, this->m_pinene_pct, this->enforceMinAndMax(val, "pinene_pct", 0.0, 100.0)); } -void Hop::setPolyphenols_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::polyphenols_pct, this->m_polyphenols_pct, this->enforceMinAndMax(val, "polyphenols_pct", 0.0, 100.0)); } -void Hop::setXanthohumol_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::xanthohumol_pct, this->m_xanthohumol_pct, this->enforceMinAndMax(val, "xanthohumol_pct", 0.0, 100.0)); } +void Hop::setProducer (QString const & val) { this->setAndNotify(PropertyNames::Hop::producer, this->m_producer, val ); } +void Hop::setProduct_id (QString const & val) { this->setAndNotify(PropertyNames::Hop::product_id, this->m_product_id, val ); } +void Hop::setYear (std::optional const val) { this->setAndNotify(PropertyNames::Hop::year, this->m_year, val ); } +void Hop::setTotal_oil_ml_per_100g(std::optional const val) { this->setAndNotify(PropertyNames::Hop::total_oil_ml_per_100g, this->m_total_oil_ml_per_100g, this->enforceMinAndMax(val, "total_oil_ml_per_100g", 0.0, 100.0)); } +void Hop::setFarnesene_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::farnesene_pct, this->m_farnesene_pct, this->enforceMinAndMax(val, "farnesene_pct", 0.0, 100.0)); } +void Hop::setGeraniol_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::geraniol_pct, this->m_geraniol_pct, this->enforceMinAndMax(val, "geraniol_pct", 0.0, 100.0)); } +void Hop::setB_pinene_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::b_pinene_pct, this->m_b_pinene_pct, this->enforceMinAndMax(val, "b_pinene_pct", 0.0, 100.0)); } +void Hop::setLinalool_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::linalool_pct, this->m_linalool_pct, this->enforceMinAndMax(val, "linalool_pct", 0.0, 100.0)); } +void Hop::setLimonene_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::limonene_pct, this->m_limonene_pct, this->enforceMinAndMax(val, "limonene_pct", 0.0, 100.0)); } +void Hop::setNerol_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::nerol_pct, this->m_nerol_pct, this->enforceMinAndMax(val, "nerol_pct", 0.0, 100.0)); } +void Hop::setPinene_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::pinene_pct, this->m_pinene_pct, this->enforceMinAndMax(val, "pinene_pct", 0.0, 100.0)); } +void Hop::setPolyphenols_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::polyphenols_pct, this->m_polyphenols_pct, this->enforceMinAndMax(val, "polyphenols_pct", 0.0, 100.0)); } +void Hop::setXanthohumol_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::xanthohumol_pct, this->m_xanthohumol_pct, this->enforceMinAndMax(val, "xanthohumol_pct", 0.0, 100.0)); } void Hop::setInventoryAmount(double num) { InventoryUtils::setAmount(*this, num); } diff --git a/src/model/Hop.h b/src/model/Hop.h index bdede5f2..ffe83125 100644 --- a/src/model/Hop.h +++ b/src/model/Hop.h @@ -225,17 +225,17 @@ class Hop : public NamedEntityWithInventory { // .:TODO JSON:. Some of these should be optional Q_PROPERTY(QString producer READ producer WRITE setProducer ) Q_PROPERTY(QString product_id READ product_id WRITE setProduct_id ) - Q_PROPERTY(std::optional year READ year WRITE setYear ) - Q_PROPERTY(std::optional total_oil_ml_per_100g READ total_oil_ml_per_100g WRITE setTotal_oil_ml_per_100g) - Q_PROPERTY(std::optional farnesene_pct READ farnesene_pct WRITE setFarnesene_pct ) - Q_PROPERTY(std::optional geraniol_pct READ geraniol_pct WRITE setGeraniol_pct ) - Q_PROPERTY(std::optional b_pinene_pct READ b_pinene_pct WRITE setB_pinene_pct ) - Q_PROPERTY(std::optional linalool_pct READ linalool_pct WRITE setLinalool_pct ) - Q_PROPERTY(std::optional limonene_pct READ limonene_pct WRITE setLimonene_pct ) - Q_PROPERTY(std::optional nerol_pct READ nerol_pct WRITE setNerol_pct ) - Q_PROPERTY(std::optional pinene_pct READ pinene_pct WRITE setPinene_pct ) - Q_PROPERTY(std::optional polyphenols_pct READ polyphenols_pct WRITE setPolyphenols_pct ) - Q_PROPERTY(std::optional xanthohumol_pct READ xanthohumol_pct WRITE setXanthohumol_pct ) + Q_PROPERTY(std::optional year READ year WRITE setYear ) + Q_PROPERTY(std::optional total_oil_ml_per_100g READ total_oil_ml_per_100g WRITE setTotal_oil_ml_per_100g) + Q_PROPERTY(std::optional farnesene_pct READ farnesene_pct WRITE setFarnesene_pct ) + Q_PROPERTY(std::optional geraniol_pct READ geraniol_pct WRITE setGeraniol_pct ) + Q_PROPERTY(std::optional b_pinene_pct READ b_pinene_pct WRITE setB_pinene_pct ) + Q_PROPERTY(std::optional linalool_pct READ linalool_pct WRITE setLinalool_pct ) + Q_PROPERTY(std::optional limonene_pct READ limonene_pct WRITE setLimonene_pct ) + Q_PROPERTY(std::optional nerol_pct READ nerol_pct WRITE setNerol_pct ) + Q_PROPERTY(std::optional pinene_pct READ pinene_pct WRITE setPinene_pct ) + Q_PROPERTY(std::optional polyphenols_pct READ polyphenols_pct WRITE setPolyphenols_pct ) + Q_PROPERTY(std::optional xanthohumol_pct READ xanthohumol_pct WRITE setXanthohumol_pct ) //============================================ "GETTER" MEMBER FUNCTIONS ============================================ double alpha_pct () const; @@ -254,19 +254,19 @@ class Hop : public NamedEntityWithInventory { double cohumulone_pct () const; double myrcene_pct () const; // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - QString producer () const; - QString product_id () const; - std::optional year () const; - std::optional total_oil_ml_per_100g() const; - std::optional farnesene_pct () const; - std::optional geraniol_pct () const; - std::optional b_pinene_pct () const; - std::optional linalool_pct () const; - std::optional limonene_pct () const; - std::optional nerol_pct () const; - std::optional pinene_pct () const; - std::optional polyphenols_pct () const; - std::optional xanthohumol_pct () const; + QString producer () const; + QString product_id () const; + std::optional year () const; + std::optional total_oil_ml_per_100g() const; + std::optional farnesene_pct () const; + std::optional geraniol_pct () const; + std::optional b_pinene_pct () const; + std::optional linalool_pct () const; + std::optional limonene_pct () const; + std::optional nerol_pct () const; + std::optional pinene_pct () const; + std::optional polyphenols_pct () const; + std::optional xanthohumol_pct () const; virtual double inventory() const; @@ -287,19 +287,19 @@ class Hop : public NamedEntityWithInventory { void setCohumulone_pct (double const val); void setMyrcene_pct (double const val); // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - void setProducer (QString const & val); - void setProduct_id (QString const & val); - void setYear (std::optional const val); - void setTotal_oil_ml_per_100g(std::optional const val); - void setFarnesene_pct (std::optional const val); - void setGeraniol_pct (std::optional const val); - void setB_pinene_pct (std::optional const val); - void setLinalool_pct (std::optional const val); - void setLimonene_pct (std::optional const val); - void setNerol_pct (std::optional const val); - void setPinene_pct (std::optional const val); - void setPolyphenols_pct (std::optional const val); - void setXanthohumol_pct (std::optional const val); + void setProducer (QString const & val); + void setProduct_id (QString const & val); + void setYear (std::optional const val); + void setTotal_oil_ml_per_100g(std::optional const val); + void setFarnesene_pct (std::optional const val); + void setGeraniol_pct (std::optional const val); + void setB_pinene_pct (std::optional const val); + void setLinalool_pct (std::optional const val); + void setLimonene_pct (std::optional const val); + void setNerol_pct (std::optional const val); + void setPinene_pct (std::optional const val); + void setPolyphenols_pct (std::optional const val); + void setXanthohumol_pct (std::optional const val); virtual void setInventoryAmount(double const val); @@ -328,17 +328,17 @@ class Hop : public NamedEntityWithInventory { // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ QString m_producer; QString m_product_id; - std::optional m_year; - std::optional m_total_oil_ml_per_100g; - std::optional m_farnesene_pct; - std::optional m_geraniol_pct; - std::optional m_b_pinene_pct; - std::optional m_linalool_pct; - std::optional m_limonene_pct; - std::optional m_nerol_pct; - std::optional m_pinene_pct; - std::optional m_polyphenols_pct; - std::optional m_xanthohumol_pct; + std::optional m_year; + std::optional m_total_oil_ml_per_100g; + std::optional m_farnesene_pct; + std::optional m_geraniol_pct; + std::optional m_b_pinene_pct; + std::optional m_linalool_pct; + std::optional m_limonene_pct; + std::optional m_nerol_pct; + std::optional m_pinene_pct; + std::optional m_polyphenols_pct; + std::optional m_xanthohumol_pct; void setDefaults(); }; diff --git a/src/widgets/SmartLineEdit.cpp b/src/widgets/SmartLineEdit.cpp index b4c220f5..d79af0ec 100644 --- a/src/widgets/SmartLineEdit.cpp +++ b/src/widgets/SmartLineEdit.cpp @@ -44,6 +44,7 @@ class SmartLineEdit::impl { impl(SmartLineEdit & self) : m_self {self}, m_initialised {false}, + m_name {"Uninitialised!"}, m_typeInfo {nullptr}, m_buddyLabel {nullptr}, m_uiAmountWithUnits {nullptr}, @@ -108,16 +109,29 @@ class SmartLineEdit::impl { * \brief We want to have two different signatures of \c SmartLineEdit::init so we can catch missing parameters at * compile time. Ultimately they both do pretty much the same work, by calling this function. */ - void init(TypeInfo const & typeInfo, - SmartLabel * buddyLabel, - int const precision, - QString const & maximalDisplayString) { + void init(char const * const name, + TypeInfo const & typeInfo, + SmartLabel * buddyLabel, + std::optional const precision, + QString const & maximalDisplayString) { // It's a coding error to call this function twice on the same object, ie we should only initialise something once! Q_ASSERT(!this->m_initialised); - this->m_typeInfo = &typeInfo; - this->m_buddyLabel = buddyLabel; - this->m_precision = precision; + this->m_name = name; + this->m_typeInfo = &typeInfo; + this->m_buddyLabel = buddyLabel; + if (precision) { + // It's a coding error to specify precision for a field that's not a (possibly optional) double (or a float, + // but we don't use float) + Q_ASSERT(this->m_typeInfo->typeIndex == typeid(double) || + this->m_typeInfo->typeIndex == typeid(std::optional)); + this->m_precision = *precision; + } + // For integers, there are no decimal places to show + if (this->m_typeInfo->typeIndex == typeid(int) || + this->m_typeInfo->typeIndex == typeid(unsigned int)) { + this->m_precision = 0; + } this->m_maximalDisplayString = maximalDisplayString; this->m_initialised = true; @@ -146,11 +160,25 @@ class SmartLineEdit::impl { return; } + /** + * \brief You can't pass in std::nullopt to setAmount as it's of type std::nullopt_t, so this saves us repeating some + * code. + */ + void setNoAmount() { + // What the field is measuring doesn't matter as it's not set + this->m_self.QLineEdit::setText(""); + this->setDisplaySize(); + return; + } + SmartLineEdit & m_self; bool m_initialised; + char const * m_name; TypeInfo const * m_typeInfo; SmartLabel * m_buddyLabel; std::unique_ptr m_uiAmountWithUnits; + // "Precision" (ie number of decimal places to show) is used if and only the field is numeric. For int and unsigned + // int, it must always be 0. int m_precision; QString m_maximalDisplayString; int m_desiredWidthInPixels; @@ -167,11 +195,12 @@ SmartLineEdit::SmartLineEdit(QWidget * parent) : QLineEdit(parent), pimpl{std::m SmartLineEdit::~SmartLineEdit() = default; -void SmartLineEdit::init(TypeInfo const & typeInfo, - SmartLabel & buddyLabel, - int const precision, - QString const & maximalDisplayString) { - qDebug() << Q_FUNC_INFO << typeInfo; +void SmartLineEdit::init(char const * const name, + TypeInfo const & typeInfo, + SmartLabel & buddyLabel, + std::optional const precision, + QString const & maximalDisplayString) { + qDebug() << Q_FUNC_INFO << name << ":" << typeInfo; // It's a coding error to call this version of init with a NonPhysicalQuantity Q_ASSERT(typeInfo.fieldType && !std::holds_alternative(*typeInfo.fieldType)); @@ -185,19 +214,21 @@ void SmartLineEdit::init(TypeInfo const & typeInfo, if (!this->pimpl->m_editField .isEmpty()) { this->pimpl->m_uiAmountWithUnits->setEditField (this->pimpl->m_editField ); } if (!this->pimpl->m_configSection.isEmpty()) { this->pimpl->m_uiAmountWithUnits->setConfigSection(this->pimpl->m_configSection); } - this->pimpl->init(typeInfo, &buddyLabel, precision, maximalDisplayString); + this->pimpl->init(name, typeInfo, &buddyLabel, precision, maximalDisplayString); return; } -void SmartLineEdit::init(TypeInfo const & typeInfo, - int const precision, - QString const & maximalDisplayString) { - qDebug() << Q_FUNC_INFO << typeInfo; +void SmartLineEdit::init(char const * const name, + TypeInfo const & typeInfo, + std::optional const precision, + QString const & maximalDisplayString) { + qDebug() << Q_FUNC_INFO << name << ":" << typeInfo; - // It's a coding error to call this version of init with anything other than a NonPhysicalQuantity + // It's a coding error to call this version of init with anything other than a NonPhysicalQuantity. (If you hit this + // assert, the debug log above should tell you which field is emitting it!) Q_ASSERT(typeInfo.fieldType && std::holds_alternative(*typeInfo.fieldType)); - this->pimpl->init(typeInfo, nullptr, precision, maximalDisplayString); + this->pimpl->init(name, typeInfo, nullptr, precision, maximalDisplayString); return; } @@ -223,44 +254,77 @@ Measurement::Amount SmartLineEdit::toCanonical() const { return this->pimpl->m_uiAmountWithUnits->rawToCanonical(this->text()); } -void SmartLineEdit::setAmount(std::optional amount) { +template +void SmartLineEdit::setAmount(std::optional amount) { Q_ASSERT(this->pimpl->m_initialised); + if (this->pimpl->m_typeInfo->typeIndex != typeid(T)) { + qCritical() << + Q_FUNC_INFO << this->pimpl->m_name << ": Trying to set wrong type; m_typeInfo=" << this->pimpl->m_typeInfo; + } + if (!amount) { - // What the field is measuring doesn't matter as it's not set - this->QLineEdit::setText(""); - } else { - if (std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)) { - // The field is not measuring a physical quantity so there are no units or unit conversions to handle - - NonPhysicalQuantity const nonPhysicalQuantity = - std::get(*this->pimpl->m_typeInfo->fieldType); - // It's a coding error if we're trying to pass a number in to a string field - Q_ASSERT(nonPhysicalQuantity != NonPhysicalQuantity::String); - - // For percentages, we'd like to show the % symbol after the number - QString symbol{""}; - if (NonPhysicalQuantity::Percentage == nonPhysicalQuantity) { - symbol = " %"; - } + this->pimpl->setNoAmount(); + return; + } - this->QLineEdit::setText( - Measurement::displayQuantity(*amount, this->pimpl->m_precision) + symbol - ); - } else { - // The field is measuring a physical quantity - this->QLineEdit::setText( - this->pimpl->m_uiAmountWithUnits->displayAmount(*amount, this->pimpl->m_precision) - ); + this->setAmount(*amount); + return; +} + +// +// Instantiate the above template function 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.) +// +template void SmartLineEdit::setAmount(std::optional amount); +template void SmartLineEdit::setAmount(std::optional amount); +template void SmartLineEdit::setAmount(std::optional amount); + +template void SmartLineEdit::setAmount(T amount) { + Q_ASSERT(this->pimpl->m_initialised); + + if (this->pimpl->m_typeInfo->typeIndex != typeid(T)) { + qCritical() << + Q_FUNC_INFO << this->pimpl->m_name << ": Trying to set wrong type; m_typeInfo=" << this->pimpl->m_typeInfo; + } + + if (std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)) { + // The field is not measuring a physical quantity so there are no units or unit conversions to handle + + NonPhysicalQuantity const nonPhysicalQuantity = + std::get(*this->pimpl->m_typeInfo->fieldType); + // It's a coding error if we're trying to pass a number in to a string field + Q_ASSERT(nonPhysicalQuantity != NonPhysicalQuantity::String); + + // For percentages, we'd like to show the % symbol after the number + QString symbol{""}; + if (NonPhysicalQuantity::Percentage == nonPhysicalQuantity) { + symbol = " %"; } + + this->QLineEdit::setText( + Measurement::displayQuantity(amount, this->pimpl->m_precision) + symbol + ); + } else { + // The field is measuring a physical quantity + this->QLineEdit::setText( + this->pimpl->m_uiAmountWithUnits->displayAmount(amount, this->pimpl->m_precision) + ); } this->pimpl->setDisplaySize(); return; } +template void SmartLineEdit::setAmount(int amount); +template void SmartLineEdit::setAmount(unsigned int amount); +template void SmartLineEdit::setAmount(double amount); + template T SmartLineEdit::getValueAs() const { - qDebug() << Q_FUNC_INFO << "Converting" << this->text() << "to" << Measurement::extractRawFromString(this->text()); + qDebug() << + Q_FUNC_INFO << this->pimpl->m_name << ": Converting" << this->text() << "to" << + Measurement::extractRawFromString(this->text()); return Measurement::extractRawFromString(this->text()); } // @@ -294,7 +358,7 @@ void SmartLineEdit::onLineChanged() { // .:TODO:. Finish work on optional if ("" == this->text() && this->pimpl->m_typeInfo->isOptional()) { - this->setAmount(std::nullopt); + this->pimpl->setNoAmount(); } else if (std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)) { // The field is not measuring a physical quantity so there are no units or unit conversions to handle // However, for anything other than a string, we still want to parse out the numeric part of the input @@ -312,17 +376,18 @@ void SmartLineEdit::onLineChanged() { } else if (this->pimpl->m_typeInfo->typeIndex == typeid(int)) { int amount = Measurement::extractRawFromString(this->text(), &ok); this->setAmount(amount); - } else if (this->pimpl->m_typeInfo->typeIndex == typeid(uint)) { - uint amount = Measurement::extractRawFromString(this->text(), &ok); + } else if (this->pimpl->m_typeInfo->typeIndex == typeid(unsigned int)) { + unsigned int amount = Measurement::extractRawFromString(this->text(), &ok); this->setAmount(amount); } else { // It's a coding error if we get here - qCritical() << Q_FUNC_INFO << "Don't know how to parse" << this->pimpl->m_typeInfo; + qCritical() << Q_FUNC_INFO << this->pimpl->m_name << ": Don't know how to parse" << this->pimpl->m_typeInfo; Q_ASSERT(false); } if (!ok) { qWarning() << - Q_FUNC_INFO << "Unable to extract number from" << this->text() << "for" << this->pimpl->m_typeInfo; + Q_FUNC_INFO << this->pimpl->m_name << ": Unable to extract number from" << this->text() << "for" << + this->pimpl->m_typeInfo; this->setAmount(0); } } @@ -336,9 +401,10 @@ void SmartLineEdit::onLineChanged() { // The field is measuring a physical quantity Q_ASSERT(this->pimpl->m_uiAmountWithUnits); qDebug() << - Q_FUNC_INFO << "Field Type:" << *this->pimpl->m_typeInfo->fieldType << ", forcedSystemOfMeasurement=" << - this->pimpl->m_uiAmountWithUnits->getForcedSystemOfMeasurement() << ", forcedRelativeScale=" << - this->pimpl->m_uiAmountWithUnits->getForcedRelativeScale() << ", value=" << this->text(); + Q_FUNC_INFO << this->pimpl->m_name << ": Field Type:" << *this->pimpl->m_typeInfo->fieldType << + ", forcedSystemOfMeasurement=" << this->pimpl->m_uiAmountWithUnits->getForcedSystemOfMeasurement() << + ", forcedRelativeScale=" << this->pimpl->m_uiAmountWithUnits->getForcedRelativeScale() << ", value=" << + this->text(); Measurement::PhysicalQuantities const physicalQuantities = ConvertToPhysicalQuantities(*this->pimpl->m_typeInfo->fieldType); @@ -368,7 +434,7 @@ void SmartLineEdit::lineChanged(PreviousScaleInfo previousScaleInfo) { // being changed. I am hoping this short circuits properly and we do // nothing if nothing changed. if (this->sender() == this && !this->isModified()) { - qDebug() << Q_FUNC_INFO << "Nothing changed; field holds" << this->text(); + qDebug() << Q_FUNC_INFO << this->pimpl->m_name << ": Nothing changed; field holds" << this->text(); return; } diff --git a/src/widgets/SmartLineEdit.h b/src/widgets/SmartLineEdit.h index c332b8a1..910747e3 100644 --- a/src/widgets/SmartLineEdit.h +++ b/src/widgets/SmartLineEdit.h @@ -89,25 +89,30 @@ class SmartLineEdit : public QLineEdit { * * This version is for a \c PhysicalQuantity (or \c Mixed2PhysicalQuantities) field * + * \param name Used only for logging, mostly for debugging. (We have hundreds of instances of this object and + * if we detect that one of them is misconfigured, it's very useful to be able to log which one!) * \param typeInfo Tells us what data type we use to store the contents of the field (when converted to * canonical units if it is a \c PhysicalQuantity) and, whether this is an optional * field (in which case we need to handle blank / empty string as a valid value). * \param buddyLabel Required if \c fieldType is \b not a \c NonPhysicalQuantity - * \param precision Where relevant determines the number of decimal places to show + * \param precision For a decimal field, this determines the number of decimal places to show. If not + * specified, we show 3 decimal places. * \param maximalDisplayString Used for determining the width of the widget */ - void init(TypeInfo const & typeInfo, - SmartLabel & buddyLabel, - int const precision = 3, - QString const & maximalDisplayString = "100.000 srm"); + void init(char const * const name, + TypeInfo const & typeInfo, + SmartLabel & buddyLabel, + std::optional const precision = std::nullopt, + QString const & maximalDisplayString = "100.000 srm"); /** * \brief As above, but for non-physical quantity such as \c NonPhysicalQuantity::Date, * \c NonPhysicalQuantity::String, etc. */ - void init(TypeInfo const & typeInfo, - int const precision = 3, - QString const & maximalDisplayString = "100.000 srm"); + void init(char const * const name, + TypeInfo const & typeInfo, + std::optional const precision = std::nullopt, + QString const & maximalDisplayString = "100.000 srm"); BtFieldType const getFieldType() const; @@ -127,11 +132,12 @@ class SmartLineEdit : public QLineEdit { Measurement::Amount toCanonical() const; /** - * \brief Set the amount for a decimal field + * \brief Set the amount for a numeric field * * \param amount is the amount to display, but the field should be blank if this is \b std::nullopt */ - void setAmount(std::optional amount); + template void setAmount(std::optional amount); + template void setAmount(T amount); // /** // * \brief Set the text for a non-numeric field @@ -189,4 +195,43 @@ public slots: std::unique_ptr pimpl; }; +/** + * \brief Helper macro for \c SMART_LINE_EDIT_INIT. Essentially does concatenation, using the magic, that, for the + * compiler, there is no difference between writing a string literal as: + * "foobarhumbug" + * and writing it as: + * "foo" "bar" "humbug" + */ +#define SLE_LOG_NAME(editorClass, fieldName) #editorClass "->" #fieldName + +/** + * \brief This macro saves a bit of copy-and-paste when invoking \c SmartLineEdit::init. Eg instead of writing: + * + * this->lineEdit_color->init("FermentableEditor->lineEdit_color", Fermentable::typeLookup.getType(PropertyNames::Fermentable::color_srm), *this->label_color, 0); + * + * you write: + * + * SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_color, PropertyNames::Fermentable::color_srm, *this->label_color, 0); + * + * \param editorClass The class name of the class holding the field we're initialising, eg \c HopEditor. (In theory we + * could pick this up via \c staticMetaObject.className(), but then we wouldn't be able to do the + * macro concatenation here.) + * \param modelClass The subclass of \c NamedEntity that we're editing. Eg in \c HopEditor, this will be \c Hop + * \param fieldName The name of the member variable for this field, eg in \c HopEditor, this could be \c lineEdit_name, + * \c lineEdit_alpha, etc. Note that: + * - We deliberately don't try to do anything clever with automatically inserting the "lineEdit_" + * prefix, as this would make the code harder to read and search. + * - It is intentional that field names sometimes differ slightly from property names. The latter + * always include their canonical unit names (eg \c PropertyNames::Fermentable::color_srm) whereas + * the former do not (eg \c lineEdit_color) because the user can enter data in any supported + * units. + * \param propertyName The name of the property to which this field relates, eg in \c HopEditor, this could be + * \c PropertyNames::NamedEntity::name, \c PropertyNames::Hop::alpha_pct, etc. (Note, as above, we + * intentionally do \b not automatically insert the \c PropertyNames:: prefix.) + * \param ... Any remaining arguments are passed through to \c SmartLineEdit::init in third position and above + * Note that the introduction of __VA_OPT__ in C++20 makes our lives easier here. + */ +#define SMART_LINE_EDIT_INIT(editorClass, modelClass, fieldName, propertyName, ...) \ + this-> fieldName ->init(SLE_LOG_NAME(editorClass, fieldName), modelClass ::typeLookup.getType(propertyName) __VA_OPT__(, __VA_ARGS__)) + #endif diff --git a/ui/hopEditor.ui b/ui/hopEditor.ui index c9e28c2c..6264785e 100644 --- a/ui/hopEditor.ui +++ b/ui/hopEditor.ui @@ -85,7 +85,7 @@
- + Alpha acids as percent by mass @@ -95,7 +95,7 @@ - + Name @@ -168,7 +168,7 @@ - + Qt::CustomContextMenu @@ -181,7 +181,7 @@ - + 0 @@ -207,7 +207,7 @@ - + 0 @@ -230,7 +230,7 @@ - + 0 @@ -256,7 +256,7 @@ - + 0 @@ -279,7 +279,7 @@ - + 0 @@ -292,7 +292,7 @@ - + Qt::CustomContextMenu @@ -305,7 +305,7 @@ - + 0 @@ -338,7 +338,7 @@ - + 0 @@ -364,7 +364,7 @@ - + 0 @@ -396,7 +396,7 @@ - + 0 @@ -428,7 +428,7 @@ - + 0 @@ -460,7 +460,7 @@ - + 0 @@ -492,7 +492,7 @@ - + 0 @@ -524,7 +524,7 @@ - + 0 @@ -556,7 +556,7 @@ - + 0 @@ -588,7 +588,7 @@ - + 0 @@ -620,7 +620,7 @@ - + 0 @@ -652,7 +652,7 @@ - + 0 @@ -684,7 +684,7 @@ - + 0 @@ -716,7 +716,7 @@ - + 0 @@ -748,7 +748,7 @@ - + 0 @@ -780,7 +780,7 @@ - + 0 @@ -812,7 +812,7 @@ - + 0 @@ -965,50 +965,23 @@ - BtGenericEdit - QLineEdit -
BtLineEdit.h
-
- - BtStringEdit - QLineEdit -
BtLineEdit.h
-
- - BtTimeEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtTimeLabel + SmartLabel QLabel -
BtLabel.h
+
widgets/SmartLabel.h
changedSystemOfMeasurementOrScale(PreviousScaleInfo)
- BtMassEdit + SmartLineEdit QLineEdit -
BtAmountEdit.h
+
widgets/SmartLineEdit.h
textModified() lineChanged() lineChanged(PreviousScaleInfo)
- - BtMassLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - popContextMenu(QPoint) - -
tabWidget_editor From cf54aa60b42448ce4bf805f2d420369e8862b5eb Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sun, 2 Apr 2023 18:47:42 +0200 Subject: [PATCH 14/42] Add SmartLineEdit etc to Style Editor --- src/BrewNoteWidget.cpp | 11 ----- src/FermentableEditor.cpp | 13 ------ src/MiscEditor.cpp | 4 -- src/StyleEditor.cpp | 61 ++++++++++++++++--------- src/model/Style.cpp | 40 ++++++++--------- ui/styleEditor.ui | 93 +++++++++++---------------------------- 6 files changed, 86 insertions(+), 136 deletions(-) diff --git a/src/BrewNoteWidget.cpp b/src/BrewNoteWidget.cpp index fabb3230..ec2fc2cc 100644 --- a/src/BrewNoteWidget.cpp +++ b/src/BrewNoteWidget.cpp @@ -49,17 +49,6 @@ BrewNoteWidget::BrewNoteWidget(QWidget *parent) : QWidget(parent) { SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_volIntoBk , PropertyNames::BrewNote::volumeIntoBK_l , *this->label_volIntoBk ); SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_volIntoFerm, PropertyNames::BrewNote::volumeIntoFerm_l, *this->label_volIntoFerm); -/// this->lineEdit_Fg ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::fg ), *this->label_Fg ); -/// this->lineEdit_Og ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::og ), *this->label_Og ); -/// this->lineEdit_Sg ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::sg ), *this->label_Sg ); -/// this->lineEdit_mashFinTemp->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::mashFinTemp_c ), *this->label_mashFinTemp); -/// this->lineEdit_pitchTemp ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::pitchTemp_c ), *this->label_pitchTemp ); -/// this->lineEdit_strikeTemp ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::strikeTemp_c ), *this->label_strikeTemp ); -/// this->lineEdit_finalVolume->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::finalVolume_l ), *this->label_finalVolume ); -/// this->lineEdit_postBoilVol->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::postBoilVolume_l), *this->label_postBoilVol); -/// this->lineEdit_volIntoBk ->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::volumeIntoBK_l ), *this->label_volIntoBk ); -/// this->lineEdit_volIntoFerm->init(BrewNote::typeLookup.getType(PropertyNames::BrewNote::volumeIntoFerm_l), *this->label_volIntoFerm); - connect(this->lineEdit_Sg, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateSG ); connect(this->lineEdit_volIntoBk, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateVolumeIntoBK_l ); connect(this->lineEdit_strikeTemp, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateStrikeTemp_c ); diff --git a/src/FermentableEditor.cpp b/src/FermentableEditor.cpp index a8137125..72a85fb9 100644 --- a/src/FermentableEditor.cpp +++ b/src/FermentableEditor.cpp @@ -55,19 +55,6 @@ FermentableEditor::FermentableEditor(QWidget* parent) : SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_origin , PropertyNames::Fermentable::origin ); SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_supplier , PropertyNames::Fermentable::supplier ); -// this->lineEdit_name ->init(Fermentable::typeLookup.getType(PropertyNames::NamedEntity::name ) ); -// this->lineEdit_color ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::color_srm ), *this->label_color , 0); -// this->lineEdit_diastaticPower->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::diastaticPower_lintner), *this->label_diastaticPower ); -// this->lineEdit_coarseFineDiff->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::coarseFineDiff_pct ) , 0); -// this->lineEdit_ibuGalPerLb ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::ibuGalPerLb ) , 0); -// this->lineEdit_maxInBatch ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::maxInBatch_pct ) , 0); -// this->lineEdit_moisture ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::moisture_pct ) , 0); -// this->lineEdit_protein ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::protein_pct ) , 0); -// this->lineEdit_yield ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::yield_pct ) , 1); -// this->lineEdit_inventory ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::amount ), *this->label_inventory ); -// this->lineEdit_origin ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::origin ) ); -// this->lineEdit_supplier ->init(Fermentable::typeLookup.getType(PropertyNames::Fermentable::supplier ) ); - connect(pushButton_new, &QAbstractButton::clicked, this, &FermentableEditor::clickedNewFermentable); connect(pushButton_save, &QAbstractButton::clicked, this, &FermentableEditor::save); connect(pushButton_cancel, &QAbstractButton::clicked, this, &FermentableEditor::clearAndClose); diff --git a/src/MiscEditor.cpp b/src/MiscEditor.cpp index 26a657f7..a44a8078 100644 --- a/src/MiscEditor.cpp +++ b/src/MiscEditor.cpp @@ -40,10 +40,6 @@ MiscEditor::MiscEditor(QWidget * parent) : SMART_LINE_EDIT_INIT(MiscEditor, Misc, lineEdit_inventory, PropertyNames::Misc::amount , *this->label_inventory); SMART_LINE_EDIT_INIT(MiscEditor, Misc, lineEdit_time , PropertyNames::Misc::time , *this->label_time ); -/// this->lineEdit_name ->init(Misc::typeLookup.getType(PropertyNames::NamedEntity::name) ); -/// this->lineEdit_inventory->init(Misc::typeLookup.getType(PropertyNames::Misc::amount ), *this->label_inventory); -/// this->lineEdit_time ->init(Misc::typeLookup.getType(PropertyNames::Misc::time ), *this->label_time ); - // Note, per https://wiki.qt.io/New_Signal_Slot_Syntax#Default_arguments_in_slot, the use of a trivial lambda // function to allow use of default argument on newStyle() slot connect(pushButton_new , &QAbstractButton::clicked, this, [this]() { this->newMisc(); return; } ); diff --git a/src/StyleEditor.cpp b/src/StyleEditor.cpp index 54b0326c..8a23a5c0 100644 --- a/src/StyleEditor.cpp +++ b/src/StyleEditor.cpp @@ -48,6 +48,25 @@ StyleEditor::StyleEditor(QWidget* parent, bool singleStyleEditor) : QDialog{pare styleProxyModel->setSourceModel(styleListModel); styleComboBox->setModel(styleProxyModel); + // Note that the Min / Max pairs of entry fields each share a label (which is shown to the left of both fields) + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_name , PropertyNames::NamedEntity::name ); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_category , PropertyNames::Style::category ); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_categoryNumber, PropertyNames::Style::categoryNumber ); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_styleLetter , PropertyNames::Style::styleLetter ); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_styleGuide , PropertyNames::Style::styleGuide ); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_ogMin , PropertyNames::Style::ogMin , *this->label_og ); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_ogMax , PropertyNames::Style::ogMax , *this->label_og ); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_fgMin , PropertyNames::Style::fgMin , *this->label_fg ); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_fgMax , PropertyNames::Style::fgMax , *this->label_fg ); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_ibuMin , PropertyNames::Style::ibuMin , *this->label_ibu , 0); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_ibuMax , PropertyNames::Style::ibuMax , *this->label_ibu , 0); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_colorMin , PropertyNames::Style::colorMin_srm , *this->label_color); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_colorMax , PropertyNames::Style::colorMax_srm , *this->label_color); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_carbMin , PropertyNames::Style::carbMin_vol , *this->label_carb , 0); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_carbMax , PropertyNames::Style::carbMax_vol , *this->label_carb , 0); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_abvMin , PropertyNames::Style::abvMin_pct , 1); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_abvMax , PropertyNames::Style::abvMax_pct , 1); + // Note, per https://wiki.qt.io/New_Signal_Slot_Syntax#Default_arguments_in_slot, the use of a trivial lambda // function to allow use of default argument on newStyle() slot connect(pushButton_save , &QAbstractButton::clicked , this, &StyleEditor::save ); @@ -198,28 +217,28 @@ void StyleEditor::showChanges(QMetaProperty const * metaProp) { // QVariant val = metaProp->read(this->obsStyle); } - if (updateAll || propName == PropertyNames::NamedEntity::name ) { lineEdit_name ->setText(this->obsStyle->name ()); // Continues to next line + if (updateAll || propName == PropertyNames::NamedEntity::name ) { lineEdit_name ->setText (this->obsStyle->name ()); // Continues to next line tabWidget_profile ->setTabText(0, this->obsStyle->name ()); } - if (updateAll || propName == PropertyNames::Style::category ) { lineEdit_category ->setText(this->obsStyle->category ()); } - if (updateAll || propName == PropertyNames::Style::categoryNumber) { lineEdit_categoryNumber->setText(this->obsStyle->categoryNumber()); } - if (updateAll || propName == PropertyNames::Style::styleLetter ) { lineEdit_styleLetter ->setText(this->obsStyle->styleLetter ()); } - if (updateAll || propName == PropertyNames::Style::styleGuide ) { lineEdit_styleGuide ->setText(this->obsStyle->styleGuide ()); } + if (updateAll || propName == PropertyNames::Style::category ) { lineEdit_category ->setText (this->obsStyle->category ()); } + if (updateAll || propName == PropertyNames::Style::categoryNumber) { lineEdit_categoryNumber->setText (this->obsStyle->categoryNumber()); } + if (updateAll || propName == PropertyNames::Style::styleLetter ) { lineEdit_styleLetter ->setText (this->obsStyle->styleLetter ()); } + if (updateAll || propName == PropertyNames::Style::styleGuide ) { lineEdit_styleGuide ->setText (this->obsStyle->styleGuide ()); } if (updateAll || propName == PropertyNames::Style::type ) { comboBox_type ->setCurrentIndex(static_cast(this->obsStyle->type())); } - if (updateAll || propName == PropertyNames::Style::ogMin ) { lineEdit_ogMin ->setText(this->obsStyle->ogMin ()); } - if (updateAll || propName == PropertyNames::Style::ogMax ) { lineEdit_ogMax ->setText(this->obsStyle->ogMax ()); } - if (updateAll || propName == PropertyNames::Style::fgMin ) { lineEdit_fgMin ->setText(this->obsStyle->fgMin ()); } - if (updateAll || propName == PropertyNames::Style::fgMax ) { lineEdit_fgMax ->setText(this->obsStyle->fgMax ()); } - if (updateAll || propName == PropertyNames::Style::ibuMin ) { lineEdit_ibuMin ->setText(this->obsStyle->ibuMin ()); } - if (updateAll || propName == PropertyNames::Style::ibuMax ) { lineEdit_ibuMax ->setText(this->obsStyle->ibuMax ()); } - if (updateAll || propName == PropertyNames::Style::colorMin_srm ) { lineEdit_colorMin ->setText(this->obsStyle->colorMin_srm ()); } - if (updateAll || propName == PropertyNames::Style::colorMax_srm ) { lineEdit_colorMax ->setText(this->obsStyle->colorMax_srm ()); } - if (updateAll || propName == PropertyNames::Style::carbMin_vol ) { lineEdit_carbMin ->setText(this->obsStyle->carbMin_vol ()); } - if (updateAll || propName == PropertyNames::Style::carbMax_vol ) { lineEdit_carbMax ->setText(this->obsStyle->carbMax_vol ()); } - if (updateAll || propName == PropertyNames::Style::abvMin_pct ) { lineEdit_abvMin ->setText(this->obsStyle->abvMin_pct ()); } - if (updateAll || propName == PropertyNames::Style::abvMax_pct ) { lineEdit_abvMax ->setText(this->obsStyle->abvMax_pct ()); } - if (updateAll || propName == PropertyNames::Style::profile ) { textEdit_profile ->setText(this->obsStyle->profile ()); } - if (updateAll || propName == PropertyNames::Style::ingredients ) { textEdit_ingredients ->setText(this->obsStyle->ingredients ()); } - if (updateAll || propName == PropertyNames::Style::examples ) { textEdit_examples ->setText(this->obsStyle->examples ()); } - if (updateAll || propName == PropertyNames::Style::notes ) { textEdit_notes ->setText(this->obsStyle->notes ()); } + if (updateAll || propName == PropertyNames::Style::ogMin ) { lineEdit_ogMin ->setAmount (this->obsStyle->ogMin ()); } + if (updateAll || propName == PropertyNames::Style::ogMax ) { lineEdit_ogMax ->setAmount (this->obsStyle->ogMax ()); } + if (updateAll || propName == PropertyNames::Style::fgMin ) { lineEdit_fgMin ->setAmount (this->obsStyle->fgMin ()); } + if (updateAll || propName == PropertyNames::Style::fgMax ) { lineEdit_fgMax ->setAmount (this->obsStyle->fgMax ()); } + if (updateAll || propName == PropertyNames::Style::ibuMin ) { lineEdit_ibuMin ->setAmount (this->obsStyle->ibuMin ()); } + if (updateAll || propName == PropertyNames::Style::ibuMax ) { lineEdit_ibuMax ->setAmount (this->obsStyle->ibuMax ()); } + if (updateAll || propName == PropertyNames::Style::colorMin_srm ) { lineEdit_colorMin ->setAmount (this->obsStyle->colorMin_srm ()); } + if (updateAll || propName == PropertyNames::Style::colorMax_srm ) { lineEdit_colorMax ->setAmount (this->obsStyle->colorMax_srm ()); } + if (updateAll || propName == PropertyNames::Style::carbMin_vol ) { lineEdit_carbMin ->setAmount (this->obsStyle->carbMin_vol ()); } + if (updateAll || propName == PropertyNames::Style::carbMax_vol ) { lineEdit_carbMax ->setAmount (this->obsStyle->carbMax_vol ()); } + if (updateAll || propName == PropertyNames::Style::abvMin_pct ) { lineEdit_abvMin ->setAmount (this->obsStyle->abvMin_pct ()); } + if (updateAll || propName == PropertyNames::Style::abvMax_pct ) { lineEdit_abvMax ->setAmount (this->obsStyle->abvMax_pct ()); } + if (updateAll || propName == PropertyNames::Style::profile ) { textEdit_profile ->setText (this->obsStyle->profile ()); } + if (updateAll || propName == PropertyNames::Style::ingredients ) { textEdit_ingredients ->setText (this->obsStyle->ingredients ()); } + if (updateAll || propName == PropertyNames::Style::examples ) { textEdit_examples ->setText (this->obsStyle->examples ()); } + if (updateAll || propName == PropertyNames::Style::notes ) { textEdit_notes ->setText (this->obsStyle->notes ()); } return; } diff --git a/src/model/Style.cpp b/src/model/Style.cpp index 4103b533..d74ebbf7 100644 --- a/src/model/Style.cpp +++ b/src/model/Style.cpp @@ -48,27 +48,27 @@ ObjectStore & Style::getObjectStoreTypedInstance() const { TypeLookup const Style::typeLookup { "Style", { - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::abvMax_pct , Style::m_abvMax_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::abvMin_pct , Style::m_abvMin_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::carbMax_vol , Style::m_carbMax_vol ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::carbMin_vol , Style::m_carbMin_vol ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::category , Style::m_category ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::categoryNumber, Style::m_categoryNumber), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::colorMax_srm , Style::m_colorMax_srm ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::colorMin_srm , Style::m_colorMin_srm ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::examples , Style::m_examples ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::fgMax , Style::m_fgMax ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::fgMin , Style::m_fgMin ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::ibuMax , Style::m_ibuMax ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::ibuMin , Style::m_ibuMin ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::ingredients , Style::m_ingredients ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::notes , Style::m_notes ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::ogMax , Style::m_ogMax ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::ogMin , Style::m_ogMin ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::profile , Style::m_profile ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::styleGuide , Style::m_styleGuide ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::styleLetter , Style::m_styleLetter ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::category , Style::m_category , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::categoryNumber, Style::m_categoryNumber, NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::styleLetter , Style::m_styleLetter , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::styleGuide , Style::m_styleGuide , NonPhysicalQuantity::String ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::type , Style::m_type ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::ogMin , Style::m_ogMin , Measurement::PhysicalQuantity::Density ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::ogMax , Style::m_ogMax , Measurement::PhysicalQuantity::Density ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::fgMin , Style::m_fgMin , Measurement::PhysicalQuantity::Density ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::fgMax , Style::m_fgMax , Measurement::PhysicalQuantity::Density ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::ibuMin , Style::m_ibuMin , Measurement::PhysicalQuantity::Bitterness ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::ibuMax , Style::m_ibuMax , Measurement::PhysicalQuantity::Bitterness ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::colorMin_srm , Style::m_colorMin_srm , Measurement::PhysicalQuantity::Color ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::colorMax_srm , Style::m_colorMax_srm , Measurement::PhysicalQuantity::Color ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::carbMin_vol , Style::m_carbMin_vol , Measurement::PhysicalQuantity::Carbonation ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::carbMax_vol , Style::m_carbMax_vol , Measurement::PhysicalQuantity::Carbonation ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::abvMin_pct , Style::m_abvMin_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::abvMax_pct , Style::m_abvMax_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::notes , Style::m_notes , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::profile , Style::m_profile , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::ingredients , Style::m_ingredients , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::examples , Style::m_examples , NonPhysicalQuantity::String ), // PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Style::typeString , Style::m_typeString ), }, // Parent class lookup diff --git a/ui/styleEditor.ui b/ui/styleEditor.ui index 51e91f91..08f8f915 100644 --- a/ui/styleEditor.ui +++ b/ui/styleEditor.ui @@ -116,7 +116,7 @@
- + 100 @@ -144,7 +144,7 @@ - + 100 @@ -172,7 +172,7 @@ - + 100 @@ -200,7 +200,7 @@ - + 100 @@ -356,7 +356,7 @@ - + 100 @@ -423,7 +423,7 @@ - + 100 @@ -442,7 +442,7 @@ - + 100 @@ -461,7 +461,7 @@ - + 100 @@ -487,7 +487,7 @@ - + 100 @@ -506,7 +506,7 @@ - + Qt::CustomContextMenu @@ -526,7 +526,7 @@ - + 100 @@ -545,7 +545,7 @@ - + 100 @@ -564,7 +564,7 @@ - + 100 @@ -583,7 +583,7 @@ - + 100 @@ -602,7 +602,7 @@ - + 100 @@ -621,14 +621,14 @@ - + Carb (vols) - + 100 @@ -647,7 +647,7 @@ - + Qt::CustomContextMenu @@ -660,7 +660,7 @@ - + 100 @@ -679,7 +679,7 @@ - + Qt::CustomContextMenu @@ -692,7 +692,7 @@ - + 100 @@ -718,7 +718,7 @@ - + IBUs @@ -883,58 +883,17 @@ - BtCarbonationEdit - QLineEdit -
BtAmountEdit.h
-
- - BtPercentageEdit - QLineEdit -
BtLineEdit.h
-
- - BtDimensionlessEdit - QLineEdit -
BtLineEdit.h
-
- - BtGenericEdit - QLineEdit -
BtLineEdit.h
-
- - BtStringEdit - QLineEdit -
BtLineEdit.h
-
- - BtColorLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
- - BtColorEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtDensityLabel + SmartLabel QLabel -
BtLabel.h
+
widgets/SmartLabel.h
changedSystemOfMeasurementOrScale(PreviousScaleInfo)
- BtDensityEdit + SmartLineEdit QLineEdit -
BtAmountEdit.h
+
widgets/SmartLineEdit.h
lineChanged(PreviousScaleInfo) From a8ab580d950f751a2bba05003b261519e88cc8f9 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sun, 2 Apr 2023 19:12:55 +0200 Subject: [PATCH 15/42] Add SmartLineEdit etc to Mash Editor --- src/MashEditor.cpp | 114 +++++++++++++++++--------------------------- src/StyleEditor.cpp | 56 +++++++++++----------- src/model/Mash.cpp | 16 +++---- ui/mashEditor.ui | 69 ++++++++------------------- 4 files changed, 99 insertions(+), 156 deletions(-) diff --git a/src/MashEditor.cpp b/src/MashEditor.cpp index ea8070d8..c2c3f175 100644 --- a/src/MashEditor.cpp +++ b/src/MashEditor.cpp @@ -31,9 +31,17 @@ MashEditor::MashEditor(QWidget* parent) : QDialog(parent), mashObs(nullptr) { setupUi(this); - connect(pushButton_fromEquipment, &QAbstractButton::clicked, this, &MashEditor::fromEquipment ); - connect(this, &QDialog::accepted, this, &MashEditor::saveAndClose ); - connect(this, &QDialog::rejected, this, &MashEditor::closeEditor ); + SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_name , PropertyNames::NamedEntity::name ); + SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_grainTemp , PropertyNames::Mash::grainTemp_c , *this->label_grainTemp , 1); + SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_spargeTemp, PropertyNames::Mash::spargeTemp_c , *this->label_spargeTemp, 1); + SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_spargePh , PropertyNames::Mash::ph , *this->label_spargePh , 0); + SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_tunTemp , PropertyNames::Mash::tunTemp_c , *this->label_tunTemp , 1); + SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_tunMass , PropertyNames::Mash::tunWeight_kg , *this->label_tunMass ); + SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_tunSpHeat , PropertyNames::Mash::tunSpecificHeat_calGC, *this->label_tunSpHeat , 1); + + connect(pushButton_fromEquipment, &QAbstractButton::clicked, this, &MashEditor::fromEquipment); + connect(this, &QDialog::accepted, this, &MashEditor::saveAndClose ); + connect(this, &QDialog::rejected, this, &MashEditor::closeEditor ); return; } @@ -57,17 +65,15 @@ void MashEditor::saveAndClose() { } qDebug() << Q_FUNC_INFO << "Saving" << (isNew ? "new" : "existing") << "mash (#" << this->mashObs->key() << ")"; - mashObs->setEquipAdjust(true); // BeerXML won't like me, but it's just stupid not to adjust for the equipment when you're able. - - mashObs->setName(lineEdit_name->text()); - mashObs->setGrainTemp_c(lineEdit_grainTemp->toCanonical().quantity()); - mashObs->setSpargeTemp_c(lineEdit_spargeTemp->toCanonical().quantity()); - mashObs->setPh(lineEdit_spargePh->toCanonical().quantity()); - mashObs->setTunTemp_c(lineEdit_tunTemp->toCanonical().quantity()); - mashObs->setTunWeight_kg(lineEdit_tunMass->toCanonical().quantity()); - mashObs->setTunSpecificHeat_calGC(lineEdit_tunSpHeat->toCanonical().quantity()); - - mashObs->setNotes(textEdit_notes->toPlainText()); + this->mashObs->setEquipAdjust(true); // BeerXML won't like me, but it's just stupid not to adjust for the equipment when you're able. + this->mashObs->setName (this->lineEdit_name ->text() ); + this->mashObs->setGrainTemp_c (this->lineEdit_grainTemp ->toCanonical().quantity()); + this->mashObs->setSpargeTemp_c (this->lineEdit_spargeTemp->toCanonical().quantity()); + this->mashObs->setPh (this->lineEdit_spargePh ->toCanonical().quantity()); + this->mashObs->setTunTemp_c (this->lineEdit_tunTemp ->toCanonical().quantity()); + this->mashObs->setTunWeight_kg (this->lineEdit_tunMass ->toCanonical().quantity()); + this->mashObs->setTunSpecificHeat_calGC(this->lineEdit_tunSpHeat ->toCanonical().quantity()); + this->mashObs->setNotes (this->textEdit_notes ->toPlainText() ); if (isNew) { ObjectStoreWrapper::insert(*mashObs); @@ -86,8 +92,8 @@ void MashEditor::fromEquipment() { return; } - lineEdit_tunMass->setText(m_equip); - lineEdit_tunSpHeat->setText(m_equip); + lineEdit_tunMass ->setAmount(this->m_equip->tunWeight_kg ()); + lineEdit_tunSpHeat->setAmount(this->m_equip->tunSpecificHeat_calGC()); return; } @@ -105,11 +111,12 @@ void MashEditor::setMash(Mash* mash) { return; } -void MashEditor::setRecipe(Recipe* r) { - if ( ! r ) +void MashEditor::setRecipe(Recipe * recipe) { + if (!recipe) { return; + } - this->m_rec = r; + this->m_rec = recipe; this->m_equip = this->m_rec->equipment(); if (this->mashObs && this->m_equip) { @@ -146,8 +153,7 @@ void MashEditor::showChanges(QMetaProperty* prop) { bool updateAll = false; QString propName; - if( mashObs == nullptr ) - { + if (mashObs == nullptr) { clear(); return; } @@ -159,57 +165,25 @@ void MashEditor::showChanges(QMetaProperty* prop) { } qDebug() << Q_FUNC_INFO << "Updating" << (updateAll ? "all" : "property") << propName; - if( propName == PropertyNames::NamedEntity::name || updateAll ) { - lineEdit_name->setText(mashObs->name()); - if( ! updateAll ) - return; - } - if( propName == PropertyNames::Mash::grainTemp_c || updateAll ) { - lineEdit_grainTemp->setText(mashObs); - if( ! updateAll ) - return; - } - if( propName == PropertyNames::Mash::spargeTemp_c || updateAll ) { - lineEdit_spargeTemp->setText(mashObs); - if( ! updateAll ) - return; - } - if( propName == PropertyNames::Mash::ph || updateAll ) { - lineEdit_spargePh->setText(mashObs); - if( ! updateAll ) - return; - } - if( propName == PropertyNames::Mash::tunTemp_c || updateAll ) { - lineEdit_tunTemp->setText(mashObs); - if( ! updateAll ) - return; - } - if( propName == PropertyNames::Mash::tunWeight_kg || updateAll ) { - lineEdit_tunMass->setText(mashObs); - if( ! updateAll ) - return; - } - if( propName == PropertyNames::Mash::tunSpecificHeat_calGC || updateAll ) { - lineEdit_tunSpHeat->setText(mashObs); - if( ! updateAll ) - return; - } - if( propName == PropertyNames::Mash::notes || updateAll ) { - textEdit_notes->setPlainText(mashObs->notes()); - if( ! updateAll ) - return; - } + if (updateAll || propName == PropertyNames::NamedEntity::name ) {this->lineEdit_name ->setText (mashObs->name ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Mash::grainTemp_c ) {this->lineEdit_grainTemp ->setAmount (mashObs->grainTemp_c ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Mash::spargeTemp_c ) {this->lineEdit_spargeTemp->setAmount (mashObs->spargeTemp_c ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Mash::ph ) {this->lineEdit_spargePh ->setAmount (mashObs->ph ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Mash::tunTemp_c ) {this->lineEdit_tunTemp ->setAmount (mashObs->tunTemp_c ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Mash::tunWeight_kg ) {this->lineEdit_tunMass ->setAmount (mashObs->tunWeight_kg ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Mash::tunSpecificHeat_calGC ) {this->lineEdit_tunSpHeat ->setAmount (mashObs->tunSpecificHeat_calGC()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Mash::notes ) {this->textEdit_notes ->setPlainText(mashObs->notes ()); if (!updateAll) { return; } } + return; } void MashEditor::clear() { - lineEdit_name->setText(QString("")); - lineEdit_grainTemp->setText(QString("")); - lineEdit_spargeTemp->setText(QString("")); - lineEdit_spargePh->setText(QString("")); - lineEdit_tunTemp->setText(QString("")); - lineEdit_tunMass->setText(QString("")); - lineEdit_tunSpHeat->setText(QString("")); - - textEdit_notes->setPlainText(QString("")); + this->lineEdit_name ->setText (QString("")); + this->lineEdit_grainTemp ->setText (QString("")); + this->lineEdit_spargeTemp->setText (QString("")); + this->lineEdit_spargePh ->setText (QString("")); + this->lineEdit_tunTemp ->setText (QString("")); + this->lineEdit_tunMass ->setText (QString("")); + this->lineEdit_tunSpHeat ->setText (QString("")); + this->textEdit_notes ->setPlainText(QString("")); return; } diff --git a/src/StyleEditor.cpp b/src/StyleEditor.cpp index 8a23a5c0..2f3411a1 100644 --- a/src/StyleEditor.cpp +++ b/src/StyleEditor.cpp @@ -49,19 +49,19 @@ StyleEditor::StyleEditor(QWidget* parent, bool singleStyleEditor) : QDialog{pare styleComboBox->setModel(styleProxyModel); // Note that the Min / Max pairs of entry fields each share a label (which is shown to the left of both fields) - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_name , PropertyNames::NamedEntity::name ); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_category , PropertyNames::Style::category ); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_categoryNumber, PropertyNames::Style::categoryNumber ); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_styleLetter , PropertyNames::Style::styleLetter ); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_styleGuide , PropertyNames::Style::styleGuide ); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_ogMin , PropertyNames::Style::ogMin , *this->label_og ); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_ogMax , PropertyNames::Style::ogMax , *this->label_og ); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_fgMin , PropertyNames::Style::fgMin , *this->label_fg ); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_fgMax , PropertyNames::Style::fgMax , *this->label_fg ); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_name , PropertyNames::NamedEntity::name ); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_category , PropertyNames::Style::category ); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_categoryNumber, PropertyNames::Style::categoryNumber ); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_styleLetter , PropertyNames::Style::styleLetter ); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_styleGuide , PropertyNames::Style::styleGuide ); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_ogMin , PropertyNames::Style::ogMin , *this->label_og ); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_ogMax , PropertyNames::Style::ogMax , *this->label_og ); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_fgMin , PropertyNames::Style::fgMin , *this->label_fg ); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_fgMax , PropertyNames::Style::fgMax , *this->label_fg ); SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_ibuMin , PropertyNames::Style::ibuMin , *this->label_ibu , 0); SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_ibuMax , PropertyNames::Style::ibuMax , *this->label_ibu , 0); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_colorMin , PropertyNames::Style::colorMin_srm , *this->label_color); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_colorMax , PropertyNames::Style::colorMax_srm , *this->label_color); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_colorMin , PropertyNames::Style::colorMin_srm , *this->label_color ); + SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_colorMax , PropertyNames::Style::colorMax_srm , *this->label_color ); SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_carbMin , PropertyNames::Style::carbMin_vol , *this->label_carb , 0); SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_carbMax , PropertyNames::Style::carbMax_vol , *this->label_carb , 0); SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_abvMin , PropertyNames::Style::abvMin_pct , 1); @@ -126,22 +126,22 @@ void StyleEditor::save() { this->obsStyle->setStyleLetter (lineEdit_styleLetter ->text() ); this->obsStyle->setStyleGuide (lineEdit_styleGuide ->text() ); this->obsStyle->setType (static_cast(comboBox_type->currentIndex())); - this->obsStyle->setOgMin (lineEdit_ogMin ->toCanonical().quantity() ); - this->obsStyle->setOgMax (lineEdit_ogMax ->toCanonical().quantity() ); - this->obsStyle->setFgMin (lineEdit_fgMin ->toCanonical().quantity() ); - this->obsStyle->setFgMax (lineEdit_fgMax ->toCanonical().quantity() ); - this->obsStyle->setIbuMin (lineEdit_ibuMin ->getValueAs() ); - this->obsStyle->setIbuMax (lineEdit_ibuMax ->getValueAs() ); - this->obsStyle->setColorMin_srm (lineEdit_colorMin ->toCanonical().quantity() ); - this->obsStyle->setColorMax_srm (lineEdit_colorMax ->toCanonical().quantity() ); - this->obsStyle->setCarbMin_vol (lineEdit_carbMin ->toCanonical().quantity() ); - this->obsStyle->setCarbMax_vol (lineEdit_carbMax ->toCanonical().quantity() ); - this->obsStyle->setAbvMin_pct (lineEdit_abvMin ->getValueAs() ); - this->obsStyle->setAbvMax_pct (lineEdit_abvMax ->getValueAs() ); - this->obsStyle->setProfile (textEdit_profile ->toPlainText() ); - this->obsStyle->setIngredients (textEdit_ingredients ->toPlainText() ); - this->obsStyle->setExamples (textEdit_examples ->toPlainText() ); - this->obsStyle->setNotes (textEdit_notes ->toPlainText() ); + this->obsStyle->setOgMin (lineEdit_ogMin ->toCanonical().quantity() ); + this->obsStyle->setOgMax (lineEdit_ogMax ->toCanonical().quantity() ); + this->obsStyle->setFgMin (lineEdit_fgMin ->toCanonical().quantity() ); + this->obsStyle->setFgMax (lineEdit_fgMax ->toCanonical().quantity() ); + this->obsStyle->setIbuMin (lineEdit_ibuMin ->getValueAs() ); + this->obsStyle->setIbuMax (lineEdit_ibuMax ->getValueAs() ); + this->obsStyle->setColorMin_srm (lineEdit_colorMin ->toCanonical().quantity() ); + this->obsStyle->setColorMax_srm (lineEdit_colorMax ->toCanonical().quantity() ); + this->obsStyle->setCarbMin_vol (lineEdit_carbMin ->toCanonical().quantity() ); + this->obsStyle->setCarbMax_vol (lineEdit_carbMax ->toCanonical().quantity() ); + this->obsStyle->setAbvMin_pct (lineEdit_abvMin ->getValueAs() ); + this->obsStyle->setAbvMax_pct (lineEdit_abvMax ->getValueAs() ); + this->obsStyle->setProfile (textEdit_profile ->toPlainText() ); + this->obsStyle->setIngredients (textEdit_ingredients ->toPlainText() ); + this->obsStyle->setExamples (textEdit_examples ->toPlainText() ); + this->obsStyle->setNotes (textEdit_notes ->toPlainText() ); if (this->obsStyle->key() < 0) { ObjectStoreWrapper::insert(*this->obsStyle); @@ -218,7 +218,7 @@ void StyleEditor::showChanges(QMetaProperty const * metaProp) { } if (updateAll || propName == PropertyNames::NamedEntity::name ) { lineEdit_name ->setText (this->obsStyle->name ()); // Continues to next line - tabWidget_profile ->setTabText(0, this->obsStyle->name ()); } + tabWidget_profile ->setTabText(0, this->obsStyle->name ()); } if (updateAll || propName == PropertyNames::Style::category ) { lineEdit_category ->setText (this->obsStyle->category ()); } if (updateAll || propName == PropertyNames::Style::categoryNumber) { lineEdit_categoryNumber->setText (this->obsStyle->categoryNumber()); } if (updateAll || propName == PropertyNames::Style::styleLetter ) { lineEdit_styleLetter ->setText (this->obsStyle->styleLetter ()); } diff --git a/src/model/Mash.cpp b/src/model/Mash.cpp index b5530da5..3a5b8f58 100644 --- a/src/model/Mash.cpp +++ b/src/model/Mash.cpp @@ -85,17 +85,17 @@ ObjectStore & Mash::getObjectStoreTypedInstance() const { TypeLookup const Mash::typeLookup { "Mash", { - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Mash::equipAdjust , Mash::m_equipAdjust ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Mash::grainTemp_c , Mash::m_grainTemp_c ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Mash::equipAdjust , Mash::m_equipAdjust , NonPhysicalQuantity::Bool ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Mash::grainTemp_c , Mash::m_grainTemp_c , Measurement::PhysicalQuantity::Temperature ), // PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Mash::mashSteps , Mash::m_mashSteps ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Mash::notes , Mash::m_notes ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Mash::ph , Mash::m_ph ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Mash::spargeTemp_c , Mash::m_spargeTemp_c ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Mash::notes , Mash::m_notes , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Mash::ph , Mash::m_ph , Measurement::PhysicalQuantity::Acidity ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Mash::spargeTemp_c , Mash::m_spargeTemp_c , Measurement::PhysicalQuantity::Temperature ), // PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Mash::totalMashWater_l , Mash::m_totalMashWater_l ), // Calculated, not stored // PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Mash::totalTime , Mash::m_totalTime ), // Calculated, not stored - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Mash::tunSpecificHeat_calGC, Mash::m_tunSpecificHeat_calGC), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Mash::tunTemp_c , Mash::m_tunTemp_c ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Mash::tunWeight_kg , Mash::m_tunWeight_kg ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Mash::tunSpecificHeat_calGC, Mash::m_tunSpecificHeat_calGC, Measurement::PhysicalQuantity::SpecificHeatCapacity), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Mash::tunTemp_c , Mash::m_tunTemp_c , Measurement::PhysicalQuantity::Temperature ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Mash::tunWeight_kg , Mash::m_tunWeight_kg , Measurement::PhysicalQuantity::Mass ), }, // Parent class lookup &NamedEntity::typeLookup diff --git a/ui/mashEditor.ui b/ui/mashEditor.ui index ccaaca2f..e6f0850e 100644 --- a/ui/mashEditor.ui +++ b/ui/mashEditor.ui @@ -41,7 +41,7 @@
- + 0 @@ -64,7 +64,7 @@ - + 0 @@ -83,7 +83,7 @@ - + 0 @@ -109,7 +109,7 @@ - + 0 @@ -128,7 +128,7 @@ - + 0 @@ -154,7 +154,7 @@ - + 0 @@ -170,7 +170,7 @@ - + 0 @@ -252,7 +252,7 @@ - + 0 @@ -271,7 +271,7 @@ - + 0 @@ -316,7 +316,7 @@ - + 0 @@ -335,7 +335,7 @@ - + 0 @@ -361,7 +361,7 @@ - + 0 @@ -377,7 +377,7 @@ - + 0 @@ -432,52 +432,21 @@ - BtGenericEdit - QLineEdit -
BtLineEdit.h
-
- - BtAcidityEdit - QLineEdit -
BtAmountEdit.h
-
- - BtSpecificHeatCapacityEdit - QLineEdit -
BtAmountEdit.h
-
- - BtMassEdit - QLineEdit -
BtAmountEdit.h
- - textModified() - lineChanged() - lineChanged(PreviousScaleInfo) - -
- - BtMassLabel + SmartLabel QLabel -
BtLabel.h
+
widgets/SmartLabel.h
changedSystemOfMeasurementOrScale(PreviousScaleInfo) popContextMenu(QPoint)
- BtTemperatureLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
- - BtTemperatureEdit + SmartLineEdit QLineEdit -
BtAmountEdit.h
+
widgets/SmartLineEdit.h
+ textModified() + lineChanged() lineChanged(PreviousScaleInfo)
From a4858295d9971c3ea98d298363290899e7eaee77 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Tue, 4 Apr 2023 04:17:31 +0200 Subject: [PATCH 16/42] SmartLineEdit etc for RecipeExtrasWidget --- src/MashEditor.cpp | 4 +- src/RecipeExtrasWidget.cpp | 93 +++++++----- src/measurement/PhysicalQuantity.h | 2 + src/measurement/UnitSystem.cpp | 2 + src/model/Recipe.cpp | 104 +++++++------- src/model/Recipe.h | 5 +- src/widgets/SmartLabel.cpp | 8 +- src/widgets/SmartLineEdit.h | 13 +- ui/recipeExtrasWidget.ui | 221 ++++------------------------- 9 files changed, 165 insertions(+), 287 deletions(-) diff --git a/src/MashEditor.cpp b/src/MashEditor.cpp index c2c3f175..db95bfa2 100644 --- a/src/MashEditor.cpp +++ b/src/MashEditor.cpp @@ -31,12 +31,12 @@ MashEditor::MashEditor(QWidget* parent) : QDialog(parent), mashObs(nullptr) { setupUi(this); - SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_name , PropertyNames::NamedEntity::name ); + SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_name , PropertyNames::NamedEntity::name ); SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_grainTemp , PropertyNames::Mash::grainTemp_c , *this->label_grainTemp , 1); SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_spargeTemp, PropertyNames::Mash::spargeTemp_c , *this->label_spargeTemp, 1); SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_spargePh , PropertyNames::Mash::ph , *this->label_spargePh , 0); SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_tunTemp , PropertyNames::Mash::tunTemp_c , *this->label_tunTemp , 1); - SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_tunMass , PropertyNames::Mash::tunWeight_kg , *this->label_tunMass ); + SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_tunMass , PropertyNames::Mash::tunWeight_kg , *this->label_tunMass ); SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_tunSpHeat , PropertyNames::Mash::tunSpecificHeat_calGC, *this->label_tunSpHeat , 1); connect(pushButton_fromEquipment, &QAbstractButton::clicked, this, &MashEditor::fromEquipment); diff --git a/src/RecipeExtrasWidget.cpp b/src/RecipeExtrasWidget.cpp index e5bfcdf9..ccdedcf2 100644 --- a/src/RecipeExtrasWidget.cpp +++ b/src/RecipeExtrasWidget.cpp @@ -28,27 +28,48 @@ #include "measurement/Unit.h" #include "model/Recipe.h" -RecipeExtrasWidget::RecipeExtrasWidget(QWidget* parent) : QWidget(parent), recipe(nullptr) { - setupUi(this); - - ratingChanged = false; - - connect(lineEdit_age, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updateAge ); - connect(lineEdit_ageTemp, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updateAgeTemp ); - connect(lineEdit_asstBrewer, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updateBrewerAsst ); - connect(lineEdit_brewer, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updateBrewer ); - connect(lineEdit_carbVols, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updateCarbonation ); - connect(lineEdit_primaryAge, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updatePrimaryAge ); - connect(lineEdit_primaryTemp, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updatePrimaryTemp ); - connect(lineEdit_secAge, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updateSecondaryAge ); - connect(lineEdit_secTemp, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updateSecondaryTemp); - connect(lineEdit_tertAge, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updateTertiaryAge ); - connect(lineEdit_tertTemp, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updateTertiaryTemp ); - connect(spinBox_tasteRating, QOverload::of(&QSpinBox::valueChanged), this, &RecipeExtrasWidget::changeRatings ); - connect(spinBox_tasteRating, &QAbstractSpinBox::editingFinished , this, &RecipeExtrasWidget::updateTasteRating ); - connect(dateEdit_date, &QDateTimeEdit::dateChanged , this, &RecipeExtrasWidget::updateDate ); - connect(btTextEdit_notes, &BtTextEdit::textModified , this, &RecipeExtrasWidget::updateNotes ); - connect(btTextEdit_tasteNotes, &BtTextEdit::textModified , this, &RecipeExtrasWidget::updateTasteNotes ); +RecipeExtrasWidget::RecipeExtrasWidget(QWidget* parent) : + QWidget(parent), + recipe(nullptr), + ratingChanged{false} { + + this->setupUi(this); + + SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_brewer , PropertyNames::Recipe::brewer ); + SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_asstBrewer , PropertyNames::Recipe::asstBrewer ); + SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_primaryAge , PropertyNames::Recipe::primaryAge_days , 0); + SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_primaryTemp, PropertyNames::Recipe::primaryTemp_c , *this->label_primaryTemp, 1); + SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_secAge , PropertyNames::Recipe::secondaryAge_days , 0); + SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_secTemp , PropertyNames::Recipe::secondaryTemp_c , *this->label_secTemp , 1); + SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_tertAge , PropertyNames::Recipe::tertiaryAge_days , 0); + SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_tertTemp , PropertyNames::Recipe::tertiaryTemp_c , *this->label_tertTemp , 1); + SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_age , PropertyNames::Recipe::age_days , 0); + SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_ageTemp , PropertyNames::Recipe::ageTemp_c , *this->label_ageTemp , 1); + SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_carbVols , PropertyNames::Recipe::carbonation_vols , *this->label_carbVols ); + + // See comment in model/Recipe.cpp about things we measure in days. If we switched them from Dimensionless to Time, + // we would need something like this + // this->lineEdit_primaryAge->getUiAmountWithUnits().setForcedRelativeScale(Measurement::UnitSystem::RelativeScale::Large); + // this->lineEdit_secAge ->getUiAmountWithUnits().setForcedRelativeScale(Measurement::UnitSystem::RelativeScale::Large); + // this->lineEdit_tertAge ->getUiAmountWithUnits().setForcedRelativeScale(Measurement::UnitSystem::RelativeScale::Large); + // this->lineEdit_age ->getUiAmountWithUnits().setForcedRelativeScale(Measurement::UnitSystem::RelativeScale::Large); + + connect(this->lineEdit_age , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateAge ); + connect(this->lineEdit_ageTemp , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateAgeTemp ); + connect(this->lineEdit_asstBrewer , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateBrewerAsst ); + connect(this->lineEdit_brewer , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateBrewer ); + connect(this->lineEdit_carbVols , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateCarbonation ); + connect(this->lineEdit_primaryAge , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updatePrimaryAge ); + connect(this->lineEdit_primaryTemp , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updatePrimaryTemp ); + connect(this->lineEdit_secAge , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateSecondaryAge ); + connect(this->lineEdit_secTemp , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateSecondaryTemp); + connect(this->lineEdit_tertAge , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateTertiaryAge ); + connect(this->lineEdit_tertTemp , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateTertiaryTemp ); + connect(this->spinBox_tasteRating , QOverload::of(&QSpinBox::valueChanged), this, &RecipeExtrasWidget::changeRatings ); + connect(this->spinBox_tasteRating , &QAbstractSpinBox::editingFinished , this, &RecipeExtrasWidget::updateTasteRating ); + connect(this->dateEdit_date , &QDateTimeEdit::dateChanged , this, &RecipeExtrasWidget::updateDate ); + connect(this->btTextEdit_notes , &BtTextEdit::textModified , this, &RecipeExtrasWidget::updateNotes ); + connect(this->btTextEdit_tasteNotes, &BtTextEdit::textModified , this, &RecipeExtrasWidget::updateTasteNotes ); return; } @@ -225,21 +246,21 @@ void RecipeExtrasWidget::showChanges(QMetaProperty* prop) { // "change is made" ... rinse, lather, repeat // Unlike other editors, this one needs to read from recipe when it gets an // updateAll - if (updateAll || propName == PropertyNames::Recipe::age_days ) { lineEdit_age ->setText (recipe->age_days ()); } - if (updateAll || propName == PropertyNames::Recipe::ageTemp_c ) { lineEdit_ageTemp ->setText (recipe->ageTemp_c ()); } - if (updateAll || propName == PropertyNames::Recipe::asstBrewer ) { lineEdit_asstBrewer ->setText (recipe->asstBrewer ()); } - if (updateAll || propName == PropertyNames::Recipe::brewer ) { lineEdit_brewer ->setText (recipe->brewer ()); } - if (updateAll || propName == PropertyNames::Recipe::carbonation_vols ) { lineEdit_carbVols ->setText (recipe->carbonation_vols ()); } - if (updateAll || propName == PropertyNames::Recipe::primaryAge_days ) { lineEdit_primaryAge ->setText (recipe->primaryAge_days ()); } - if (updateAll || propName == PropertyNames::Recipe::primaryTemp_c ) { lineEdit_primaryTemp ->setText (recipe->primaryTemp_c ()); } - if (updateAll || propName == PropertyNames::Recipe::secondaryAge_days) { lineEdit_secAge ->setText (recipe->secondaryAge_days()); } - if (updateAll || propName == PropertyNames::Recipe::secondaryTemp_c ) { lineEdit_secTemp ->setText (recipe->secondaryTemp_c ()); } - if (updateAll || propName == PropertyNames::Recipe::tertiaryAge_days ) { lineEdit_tertAge ->setText (recipe->tertiaryAge_days ()); } - if (updateAll || propName == PropertyNames::Recipe::tertiaryTemp_c ) { lineEdit_tertTemp ->setText (recipe->tertiaryTemp_c ()); } - if (updateAll || propName == PropertyNames::Recipe::tasteRating ) { spinBox_tasteRating ->setValue (recipe->tasteRating ()); } - if (updateAll || propName == PropertyNames::Recipe::date ) { dateEdit_date ->setDate (recipe->date ()); } - if (updateAll || propName == PropertyNames::Recipe::notes ) { btTextEdit_notes ->setPlainText(recipe->notes ()); } - if (updateAll || propName == PropertyNames::Recipe::tasteNotes ) { btTextEdit_tasteNotes->setPlainText(recipe->tasteNotes ()); } + if (updateAll || propName == PropertyNames::Recipe::age_days ) { this->lineEdit_age ->setAmount (recipe->age_days ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::ageTemp_c ) { this->lineEdit_ageTemp ->setAmount (recipe->ageTemp_c ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::asstBrewer ) { this->lineEdit_asstBrewer ->setText (recipe->asstBrewer ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::brewer ) { this->lineEdit_brewer ->setText (recipe->brewer ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::carbonation_vols ) { this->lineEdit_carbVols ->setAmount (recipe->carbonation_vols ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::primaryAge_days ) { this->lineEdit_primaryAge ->setAmount (recipe->primaryAge_days ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::primaryTemp_c ) { this->lineEdit_primaryTemp ->setAmount (recipe->primaryTemp_c ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::secondaryAge_days) { this->lineEdit_secAge ->setAmount (recipe->secondaryAge_days()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::secondaryTemp_c ) { this->lineEdit_secTemp ->setAmount (recipe->secondaryTemp_c ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::tertiaryAge_days ) { this->lineEdit_tertAge ->setAmount (recipe->tertiaryAge_days ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::tertiaryTemp_c ) { this->lineEdit_tertTemp ->setAmount (recipe->tertiaryTemp_c ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::tasteRating ) { this->spinBox_tasteRating ->setValue (recipe->tasteRating ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::date ) { this->dateEdit_date ->setDate (recipe->date ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::notes ) { this->btTextEdit_notes ->setPlainText(recipe->notes ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::tasteNotes ) { this->btTextEdit_tasteNotes->setPlainText(recipe->tasteNotes ()); if (!updateAll) { return; } } return; } diff --git a/src/measurement/PhysicalQuantity.h b/src/measurement/PhysicalQuantity.h index 2d6f3f8f..3597ba53 100644 --- a/src/measurement/PhysicalQuantity.h +++ b/src/measurement/PhysicalQuantity.h @@ -138,6 +138,8 @@ namespace Measurement { Viscosity, // Specific heat capacity -- see https://en.wikipedia.org/wiki/Specific_heat_capacity SpecificHeatCapacity, + // .:TBD:. Should we add Energy for PropertyNames::Recipe::calories (in which case, should canonical measure be + // Joules)? }; /** diff --git a/src/measurement/UnitSystem.cpp b/src/measurement/UnitSystem.cpp index 00686b46..19096293 100644 --- a/src/measurement/UnitSystem.cpp +++ b/src/measurement/UnitSystem.cpp @@ -37,6 +37,8 @@ namespace { // Used by UnitSystem::getInstanceByName() QMap nameToUnitSystem; + // .:TBD:. See if we can eliminate all this and get compile-time checking benefits + // // We sometimes want to be able to access RelativeScale enum values via a string name (eg code generated from .ui // files) so it's useful to be able to map between them. There is some functionality built in to Qt to do this via // QMetaEnum, but this is at the cost of inheriting from QObject, which seems overkill here. Alternatively, we could diff --git a/src/model/Recipe.cpp b/src/model/Recipe.cpp index 498649ce..a0d657b2 100644 --- a/src/model/Recipe.cpp +++ b/src/model/Recipe.cpp @@ -365,68 +365,76 @@ ObjectStore & Recipe::getObjectStoreTypedInstance() const { TypeLookup const Recipe::typeLookup { "Recipe", { - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::ABV_pct , Recipe::m_ABV_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::age_days , Recipe::m_age ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::ageTemp_c , Recipe::m_ageTemp_c ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::ancestorId , Recipe::m_ancestor_id ), //<< - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::asstBrewer , Recipe::m_asstBrewer ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::batchSize_l , Recipe::m_batchSize_l ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::boilGrav , Recipe::m_boilGrav ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::boilSize_l , Recipe::m_boilSize_l ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::boilTime_min , Recipe::m_boilTime_min ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::boilVolume_l , Recipe::m_boilVolume_l ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::brewer , Recipe::m_brewer ), -// PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::brewNotes , Recipe::m_brewNotes ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::calories , Recipe::m_calories ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::carbonationTemp_c , Recipe::m_carbonationTemp_c ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::carbonation_vols , Recipe::m_carbonation_vols ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::color_srm , Recipe::m_color_srm ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::date , Recipe::m_date ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::efficiency_pct , Recipe::m_efficiency_pct ), + // Note that the age_days, primaryAge_days, secondaryAge_days, tertiaryAge_days properties are dimensionless + // because: + // - It's not meaningful to measure them with greater precision + // - The canonical unit for Measurement::PhysicalQuantity::Time is Measurement::Units::minutes, so we'd have to + // either store as minutes or do some special-case handling to say we're not storing in canonical units. + // Both would be ugly + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::type , Recipe::m_type ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::brewer , Recipe::m_brewer , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::asstBrewer , Recipe::m_asstBrewer , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::batchSize_l , Recipe::m_batchSize_l , Measurement::PhysicalQuantity::Volume ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::boilTime_min , Recipe::m_boilTime_min , Measurement::PhysicalQuantity::Time ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::efficiency_pct , Recipe::m_efficiency_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::fermentationStages, Recipe::m_fermentationStages, NonPhysicalQuantity::Count ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::primaryAge_days , Recipe::m_primaryAge_days , NonPhysicalQuantity::Dimensionless ), // See comment above for why Dimensionless, not Time + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::primaryTemp_c , Recipe::m_primaryTemp_c , Measurement::PhysicalQuantity::Temperature ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::secondaryAge_days , Recipe::m_secondaryAge_days , NonPhysicalQuantity::Dimensionless ), // See comment above for why Dimensionless, not Time + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::secondaryTemp_c , Recipe::m_secondaryTemp_c , Measurement::PhysicalQuantity::Temperature ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::tertiaryAge_days , Recipe::m_tertiaryAge_days , NonPhysicalQuantity::Dimensionless ), // See comment above for why Dimensionless, not Time + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::tertiaryTemp_c , Recipe::m_tertiaryTemp_c , Measurement::PhysicalQuantity::Temperature ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::age_days , Recipe::m_age , NonPhysicalQuantity::Dimensionless ), // See comment above for why Dimensionless, not Time + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::ageTemp_c , Recipe::m_ageTemp_c , Measurement::PhysicalQuantity::Temperature ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::date , Recipe::m_date , NonPhysicalQuantity::Date ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::carbonation_vols , Recipe::m_carbonation_vols , Measurement::PhysicalQuantity::Carbonation ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::forcedCarbonation , Recipe::m_forcedCarbonation , NonPhysicalQuantity::Bool ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::primingSugarName , Recipe::m_primingSugarName , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::carbonationTemp_c , Recipe::m_carbonationTemp_c , Measurement::PhysicalQuantity::Temperature ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::primingSugarEquiv , Recipe::m_primingSugarEquiv , NonPhysicalQuantity::Dimensionless ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::kegPrimingFactor , Recipe::m_kegPrimingFactor , NonPhysicalQuantity::Dimensionless ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::notes , Recipe::m_notes , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::tasteNotes , Recipe::m_tasteNotes , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::tasteRating , Recipe::m_tasteRating , NonPhysicalQuantity::Dimensionless ), +// PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::style , Recipe::m_style ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::styleId , Recipe::styleId ), //<< +// PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::mash , Recipe::m_mash ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::mashId , Recipe::mashId ), //<< // PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::equipment , Recipe::m_equipment ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::equipmentId , Recipe::equipmentId ), //<< + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::og , Recipe::m_og , Measurement::PhysicalQuantity::Density ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::fg , Recipe::m_fg , Measurement::PhysicalQuantity::Density ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::locked , Recipe::m_locked ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::ancestorId , Recipe::m_ancestor_id ), //<< +// PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::ancestors , Recipe::m_ancestor_id ), //<< + + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::ABV_pct , Recipe::m_ABV_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::boilGrav , Recipe::m_boilGrav , Measurement::PhysicalQuantity::Density ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::boilSize_l , Recipe::m_boilSize_l , Measurement::PhysicalQuantity::Volume ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::boilVolume_l , Recipe::m_boilVolume_l , Measurement::PhysicalQuantity::Volume ), +// PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::brewNotes , Recipe::m_brewNotes ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::calories , Recipe::m_calories , NonPhysicalQuantity::Dimensionless ), // .:TBD:. One day this should perhaps become Measurement::PhysicalQuantity::Energy + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::color_srm , Recipe::m_color_srm , Measurement::PhysicalQuantity::Color ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::fermentableIds , Recipe::impl::fermentableIds), // PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::fermentables , Recipe::m_fermentables ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::fermentationStages, Recipe::m_fermentationStages), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::fg , Recipe::m_fg ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::finalVolume_l , Recipe::m_finalVolume_l ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::forcedCarbonation , Recipe::m_forcedCarbonation ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::grainsInMash_kg , Recipe::m_grainsInMash_kg ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::grains_kg , Recipe::m_grains_kg ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::finalVolume_l , Recipe::m_finalVolume_l , Measurement::PhysicalQuantity::Volume ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::grainsInMash_kg , Recipe::m_grainsInMash_kg , Measurement::PhysicalQuantity::Mass ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::grains_kg , Recipe::m_grains_kg , Measurement::PhysicalQuantity::Mass ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::hopIds , Recipe::impl::hopIds ), // PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::hops , Recipe::m_hops ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::IBU , Recipe::m_IBU ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::IBU , Recipe::m_IBU , Measurement::PhysicalQuantity::Bitterness ), // PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::IBUs , Recipe::m_IBUs ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::instructionIds , Recipe::impl::instructionIds), // PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::instructions , Recipe::m_instructions ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::kegPrimingFactor , Recipe::m_kegPrimingFactor ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::locked , Recipe::m_locked ), -// PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::mash , Recipe::m_mash ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::mashId , Recipe::mashId ), //<< - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::miscIds , Recipe::impl::miscIds ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::miscIds , Recipe::impl::miscIds ), // PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::miscs , Recipe::m_miscs ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::notes , Recipe::m_notes ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::og , Recipe::m_og ), // PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::points , Recipe::m_points ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::postBoilVolume_l , Recipe::m_postBoilVolume_l ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::primaryAge_days , Recipe::m_primaryAge_days ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::primaryTemp_c , Recipe::m_primaryTemp_c ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::primingSugarEquiv , Recipe::m_primingSugarEquiv ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::primingSugarName , Recipe::m_primingSugarName ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::postBoilVolume_l , Recipe::m_postBoilVolume_l , Measurement::PhysicalQuantity::Volume ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::saltIds , Recipe::impl::saltIds ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::secondaryAge_days , Recipe::m_secondaryAge_days ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::secondaryTemp_c , Recipe::m_secondaryTemp_c ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::SRMColor , Recipe::m_SRMColor ), -// PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::style , Recipe::m_style ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::styleId , Recipe::styleId ), //<< - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::tasteNotes , Recipe::m_tasteNotes ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::tasteRating , Recipe::m_tasteRating ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::tertiaryAge_days , Recipe::m_tertiaryAge_days ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::tertiaryTemp_c , Recipe::m_tertiaryTemp_c ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::type , Recipe::m_type ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::SRMColor , Recipe::m_SRMColor ), // NB: This is an RGB display color PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::waterIds , Recipe::impl::waterIds ), // PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::waters , Recipe::m_waters ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::wortFromMash_l , Recipe::m_wortFromMash_l ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::wortFromMash_l , Recipe::m_wortFromMash_l , Measurement::PhysicalQuantity::Volume ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::yeastIds , Recipe::impl::yeastIds ), // PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Recipe::yeasts , Recipe::m_yeasts ), }, diff --git a/src/model/Recipe.h b/src/model/Recipe.h index bb0f5b0f..07e1567c 100644 --- a/src/model/Recipe.h +++ b/src/model/Recipe.h @@ -181,7 +181,10 @@ class Recipe : public NamedEntity { Q_PROPERTY(QString notes READ notes WRITE setNotes /*NOTIFY changed*/ /*changedNotes*/) //! \brief The tasting notes. Q_PROPERTY(QString tasteNotes READ tasteNotes WRITE setTasteNotes /*NOTIFY changed*/ /*changedTasteNotes*/) - //! \brief The taste rating. + /** \brief Decimal number between zero and 50.0 denoting the taste rating – corresponds to the 50 point BJCP rating + * system. + * .:TBD:. This is stored as a double but the UI constrains it to an unsigned int. + */ Q_PROPERTY(double tasteRating READ tasteRating WRITE setTasteRating /*NOTIFY changed*/ /*changedTasteRating*/) //! \brief The number of fermentation stages. Q_PROPERTY(int fermentationStages READ fermentationStages WRITE setFermentationStages /*NOTIFY changed*/ /*changedFermentationStages*/) diff --git a/src/widgets/SmartLabel.cpp b/src/widgets/SmartLabel.cpp index 6dc5d1ad..5ad0f693 100644 --- a/src/widgets/SmartLabel.cpp +++ b/src/widgets/SmartLabel.cpp @@ -91,6 +91,13 @@ void SmartLabel::mouseReleaseEvent(QMouseEvent * event) { } void SmartLabel::textEffect(bool enabled) { + // If our buddy is an input field for a NonPhysicalQuantity, then we don't want the underline effect as there are no + // scale choices for the user to make. + auto const fieldType = this->getBuddy().getFieldType(); + if (std::holds_alternative(fieldType)) { + return; + } + QFont myFont = this->font(); myFont.setUnderline(enabled); this->setFont(myFont); @@ -103,7 +110,6 @@ void SmartLabel::initializeSection() { return; } - // // If the label has the pimpl->m_configSection defined, use it // otherwise, if the paired field has a pimpl->m_configSection, use it diff --git a/src/widgets/SmartLineEdit.h b/src/widgets/SmartLineEdit.h index 910747e3..9a3879e5 100644 --- a/src/widgets/SmartLineEdit.h +++ b/src/widgets/SmartLineEdit.h @@ -87,16 +87,23 @@ class SmartLineEdit : public QLineEdit { * above), it also ensures, if necessary, that the \c changedSystemOfMeasurementOrScale signal from the * \c SmartLabel buddy is connected to the \c lineChanged slot of this \c SmartLineEdit. * - * This version is for a \c PhysicalQuantity (or \c Mixed2PhysicalQuantities) field + * This version is for a \c PhysicalQuantity (or \c Mixed2PhysicalQuantities) field. + * + * Note, in reality, you actually use the \c SMART_LINE_EDIT_INIT macro (see below). * * \param name Used only for logging, mostly for debugging. (We have hundreds of instances of this object and * if we detect that one of them is misconfigured, it's very useful to be able to log which one!) + * * \param typeInfo Tells us what data type we use to store the contents of the field (when converted to * canonical units if it is a \c PhysicalQuantity) and, whether this is an optional * field (in which case we need to handle blank / empty string as a valid value). + * * \param buddyLabel Required if \c fieldType is \b not a \c NonPhysicalQuantity + * * \param precision For a decimal field, this determines the number of decimal places to show. If not - * specified, we show 3 decimal places. + * specified, we show 3 decimal places. TBD: IDK if one day we might need to be more + * sophisticated about this, ie with number of decimal places dependent on the units that + * the user has chosen, but for now we assume it's the same for everything. * \param maximalDisplayString Used for determining the width of the widget */ void init(char const * const name, @@ -108,6 +115,8 @@ class SmartLineEdit : public QLineEdit { /** * \brief As above, but for non-physical quantity such as \c NonPhysicalQuantity::Date, * \c NonPhysicalQuantity::String, etc. + * + * Note, in reality, you actually use the \c SMART_LINE_EDIT_INIT macro (see below). */ void init(char const * const name, TypeInfo const & typeInfo, diff --git a/ui/recipeExtrasWidget.ui b/ui/recipeExtrasWidget.ui index ec7df68a..5c3ea5ce 100644 --- a/ui/recipeExtrasWidget.ui +++ b/ui/recipeExtrasWidget.ui @@ -48,7 +48,7 @@
- + 0 @@ -109,7 +109,7 @@ - + 0 @@ -134,7 +134,7 @@ - + Qt::CustomContextMenu @@ -147,7 +147,7 @@ - + 0 @@ -169,13 +169,10 @@ primaryAge_days - - scaleLarge - - + Qt::CustomContextMenu @@ -188,7 +185,7 @@ - + 0 @@ -213,7 +210,7 @@ - + Qt::CustomContextMenu @@ -226,7 +223,7 @@ - + 0 @@ -248,13 +245,10 @@ secondaryAge_days - - scaleLarge - - + Qt::CustomContextMenu @@ -267,7 +261,7 @@ - + 0 @@ -292,7 +286,7 @@ - + Qt::CustomContextMenu @@ -305,7 +299,7 @@ - + 0 @@ -327,13 +321,10 @@ tertiaryAge_days - - scaleLarge - - + Qt::CustomContextMenu @@ -346,7 +337,7 @@ - + 0 @@ -371,7 +362,7 @@ - + Qt::CustomContextMenu @@ -384,7 +375,7 @@ - + 0 @@ -406,13 +397,10 @@ age - - scaleLarge - - + Qt::CustomContextMenu @@ -425,7 +413,7 @@ - + 0 @@ -486,7 +474,7 @@ - + Carbonation Volumes @@ -496,7 +484,7 @@ - + 0 @@ -578,43 +566,17 @@ - BtGenericEdit - QLineEdit -
BtLineEdit.h
-
- - BtTimeEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtCarbonationEdit - QLineEdit -
BtAmountEdit.h
-
- - BtTimeLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
- - BtTemperatureLabel + SmartLabel QLabel -
BtLabel.h
+
widgets/SmartLabel.h
changedSystemOfMeasurementOrScale(PreviousScaleInfo)
- BtTemperatureEdit + SmartLineEdit QLineEdit -
BtAmountEdit.h
+
widgets/SmartLineEdit.h
lineChanged(PreviousScaleInfo) @@ -624,11 +586,6 @@ QPlainTextEdit
BtTextEdit.h
- - BtStringEdit - QLineEdit -
BtLineEdit.h
-
scrollArea @@ -650,134 +607,4 @@ btTextEdit_notes - - - label_ageTemp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_ageTemp - lineChanged(PreviousScaleInfo) - - - 92 - 293 - - - 236 - 293 - - - - - label_secTemp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_secTemp - lineChanged(PreviousScaleInfo) - - - 96 - 189 - - - 236 - 189 - - - - - label_tertTemp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_tertTemp - lineChanged(PreviousScaleInfo) - - - 101 - 241 - - - 236 - 241 - - - - - label_primaryTemp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_primaryTemp - lineChanged(PreviousScaleInfo) - - - 55 - 137 - - - 206 - 137 - - - - - label_secAge - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_secAge - lineChanged(PreviousScaleInfo) - - - 90 - 150 - - - 144 - 156 - - - - - label_tertAge - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_tertAge - lineChanged(PreviousScaleInfo) - - - 103 - 205 - - - 151 - 205 - - - - - label_age - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_age - lineChanged(PreviousScaleInfo) - - - 107 - 257 - - - 209 - 255 - - - - - label_primaryAge - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_primaryAge - lineChanged(PreviousScaleInfo) - - - 93 - 94 - - - 172 - 96 - - - - From 11560bbbd6cf9359b1c054bac6bec4555a5cff25 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Tue, 4 Apr 2023 08:42:45 +0200 Subject: [PATCH 17/42] SmartLineEdit etc for PrimingDialog --- src/PrimingDialog.cpp | 28 +++++++++++----- src/widgets/SmartLineEdit.cpp | 34 ++++++++++--------- src/widgets/SmartLineEdit.h | 25 ++++++++------ ui/primingDialog.ui | 61 +++++++---------------------------- 4 files changed, 67 insertions(+), 81 deletions(-) diff --git a/src/PrimingDialog.cpp b/src/PrimingDialog.cpp index 247dadef..a8dcb07b 100644 --- a/src/PrimingDialog.cpp +++ b/src/PrimingDialog.cpp @@ -27,6 +27,13 @@ #include "measurement/Unit.h" +namespace { + auto const BATCH_SIZE = TypeInfo::construct(Measurement::PhysicalQuantity::Volume); + auto const TEMP = TypeInfo::construct(Measurement::PhysicalQuantity::Temperature); + auto const CARB_VOLS = TypeInfo::construct(Measurement::PhysicalQuantity::Carbonation); + auto const SUGAR_AMOUNT = TypeInfo::construct(Measurement::PhysicalQuantity::Mass); +} + PrimingDialog::PrimingDialog(QWidget* parent) : QDialog(parent) { this->setupUi(this); @@ -38,6 +45,11 @@ PrimingDialog::PrimingDialog(QWidget* parent) : QDialog(parent) { this->sugarGroup->addButton(radioButton_sucrose); this->sugarGroup->addButton(radioButton_dme); + SMART_LINE_EDIT_INIT_FS(PrimingDialog, lineEdit_beerVol, BATCH_SIZE , *this->label_beerVol ); + SMART_LINE_EDIT_INIT_FS(PrimingDialog, lineEdit_temp , TEMP , *this->label_temp , 1); + SMART_LINE_EDIT_INIT_FS(PrimingDialog, lineEdit_vols , CARB_VOLS , *this->label_vols , 1); + SMART_LINE_EDIT_INIT_FS(PrimingDialog, lineEdit_output , SUGAR_AMOUNT, *this->label_output ); + connect(pushButton_calculate, &QAbstractButton::clicked, this, &PrimingDialog::calculate); return; } @@ -46,17 +58,17 @@ PrimingDialog::~PrimingDialog() = default; void PrimingDialog::calculate() { - double beer_l = lineEdit_beerVol->toCanonical().quantity(); - double temp_c = lineEdit_temp->toCanonical().quantity(); - double desiredVols = lineEdit_vols->toCanonical().quantity(); + double const beer_l = lineEdit_beerVol->toCanonical().quantity(); + double const temp_c = lineEdit_temp ->toCanonical().quantity(); + double const desiredVols = lineEdit_vols ->toCanonical().quantity(); qDebug() << Q_FUNC_INFO << "Beer volume (liters):" << beer_l << ", Temp (°C):" << temp_c << ", Desired Volumes:" << desiredVols; - double residualVols = 1.57 * pow(0.97, temp_c); // Amount of CO2 still in suspension. - double addedVols = desiredVols - residualVols; - double co2_l = addedVols * beer_l; // Liters of CO2 we need to generate (at 273 K and 1 atm). - double co2_mol = co2_l / 22.4; // Mols of CO2 we need. + double const residualVols = 1.57 * pow(0.97, temp_c); // Amount of CO2 still in suspension. + double const addedVols = desiredVols - residualVols; + double const co2_l = addedVols * beer_l; // Liters of CO2 we need to generate (at 273 K and 1 atm). + double const co2_mol = co2_l / 22.4; // Mols of CO2 we need. double sugar_mol; double sugar_g; @@ -85,7 +97,7 @@ void PrimingDialog::calculate() { // The amount have to be set in default unit to BtLineEdit. // We should find a better solution, but until it is not, we must do it this way. - lineEdit_output->setText( sugar_g/1000 ); + lineEdit_output->setAmount(sugar_g/1000); return; } diff --git a/src/widgets/SmartLineEdit.cpp b/src/widgets/SmartLineEdit.cpp index d79af0ec..3d3a0d30 100644 --- a/src/widgets/SmartLineEdit.cpp +++ b/src/widgets/SmartLineEdit.cpp @@ -109,11 +109,11 @@ class SmartLineEdit::impl { * \brief We want to have two different signatures of \c SmartLineEdit::init so we can catch missing parameters at * compile time. Ultimately they both do pretty much the same work, by calling this function. */ - void init(char const * const name, - TypeInfo const & typeInfo, - SmartLabel * buddyLabel, - std::optional const precision, - QString const & maximalDisplayString) { + void init(char const * const name, + TypeInfo const & typeInfo, + SmartLabel * buddyLabel, + std::optional const precision, + QString const & maximalDisplayString) { // It's a coding error to call this function twice on the same object, ie we should only initialise something once! Q_ASSERT(!this->m_initialised); @@ -125,6 +125,10 @@ class SmartLineEdit::impl { // but we don't use float) Q_ASSERT(this->m_typeInfo->typeIndex == typeid(double) || this->m_typeInfo->typeIndex == typeid(std::optional)); + + // It's a coding error if precision is not some plausible value. For the moment at least, we assert there are + // no envisageable circumstances where we need to show more than 3 decimal places + Q_ASSERT(*precision <= 3); this->m_precision = *precision; } // For integers, there are no decimal places to show @@ -179,7 +183,7 @@ class SmartLineEdit::impl { std::unique_ptr m_uiAmountWithUnits; // "Precision" (ie number of decimal places to show) is used if and only the field is numeric. For int and unsigned // int, it must always be 0. - int m_precision; + unsigned int m_precision; QString m_maximalDisplayString; int m_desiredWidthInPixels; // .:TBD:. This is a bit ugly. We keep our own copies of fields that exist in UiAmountWithUnits because we get given @@ -195,11 +199,11 @@ SmartLineEdit::SmartLineEdit(QWidget * parent) : QLineEdit(parent), pimpl{std::m SmartLineEdit::~SmartLineEdit() = default; -void SmartLineEdit::init(char const * const name, - TypeInfo const & typeInfo, - SmartLabel & buddyLabel, - std::optional const precision, - QString const & maximalDisplayString) { +void SmartLineEdit::init(char const * const name, + TypeInfo const & typeInfo, + SmartLabel & buddyLabel, + std::optional const precision, + QString const & maximalDisplayString) { qDebug() << Q_FUNC_INFO << name << ":" << typeInfo; // It's a coding error to call this version of init with a NonPhysicalQuantity @@ -218,10 +222,10 @@ void SmartLineEdit::init(char const * const name, return; } -void SmartLineEdit::init(char const * const name, - TypeInfo const & typeInfo, - std::optional const precision, - QString const & maximalDisplayString) { +void SmartLineEdit::init(char const * const name, + TypeInfo const & typeInfo, + std::optional const precision, + QString const & maximalDisplayString) { qDebug() << Q_FUNC_INFO << name << ":" << typeInfo; // It's a coding error to call this version of init with anything other than a NonPhysicalQuantity. (If you hit this diff --git a/src/widgets/SmartLineEdit.h b/src/widgets/SmartLineEdit.h index 9a3879e5..705e19f8 100644 --- a/src/widgets/SmartLineEdit.h +++ b/src/widgets/SmartLineEdit.h @@ -106,11 +106,11 @@ class SmartLineEdit : public QLineEdit { * the user has chosen, but for now we assume it's the same for everything. * \param maximalDisplayString Used for determining the width of the widget */ - void init(char const * const name, - TypeInfo const & typeInfo, - SmartLabel & buddyLabel, - std::optional const precision = std::nullopt, - QString const & maximalDisplayString = "100.000 srm"); + void init(char const * const name, + TypeInfo const & typeInfo, + SmartLabel & buddyLabel, + std::optional const precision = std::nullopt, + QString const & maximalDisplayString = "100.000 srm"); /** * \brief As above, but for non-physical quantity such as \c NonPhysicalQuantity::Date, @@ -118,10 +118,10 @@ class SmartLineEdit : public QLineEdit { * * Note, in reality, you actually use the \c SMART_LINE_EDIT_INIT macro (see below). */ - void init(char const * const name, - TypeInfo const & typeInfo, - std::optional const precision = std::nullopt, - QString const & maximalDisplayString = "100.000 srm"); + void init(char const * const name, + TypeInfo const & typeInfo, + std::optional const precision = std::nullopt, + QString const & maximalDisplayString = "100.000 srm"); BtFieldType const getFieldType() const; @@ -243,4 +243,11 @@ public slots: #define SMART_LINE_EDIT_INIT(editorClass, modelClass, fieldName, propertyName, ...) \ this-> fieldName ->init(SLE_LOG_NAME(editorClass, fieldName), modelClass ::typeLookup.getType(propertyName) __VA_OPT__(, __VA_ARGS__)) +/** + *\brief An alternate version of \c SMART_LINE_EDIT_INIT for use when there is no \c modelClass (eg in a free-standing + * calculation dialog that does not update the model). + */ +#define SMART_LINE_EDIT_INIT_FS(editorClass, fieldName, typeInfoVar, ...) \ + this-> fieldName ->init(SLE_LOG_NAME(editorClass, fieldName), typeInfoVar __VA_OPT__(, __VA_ARGS__)) + #endif diff --git a/ui/primingDialog.ui b/ui/primingDialog.ui index 3e4790ac..af0e7555 100644 --- a/ui/primingDialog.ui +++ b/ui/primingDialog.ui @@ -26,7 +26,7 @@ - + Qt::CustomContextMenu @@ -39,7 +39,7 @@ - + 40 @@ -61,7 +61,7 @@ - + Beer Temperature @@ -71,7 +71,7 @@ - + 40 @@ -93,7 +93,7 @@ - + Desired Volumes @@ -103,7 +103,7 @@ - + 40 @@ -179,7 +179,7 @@ - + Qt::CustomContextMenu @@ -192,7 +192,7 @@ - + 80 @@ -254,55 +254,18 @@ - BtCarbonationEdit - QLineEdit -
BtAmountEdit.h
-
- - BtTemperatureEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtTemperatureLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
- - BtVolumeLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
- - BtVolumeEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtMassLabel + SmartLabel QLabel -
BtLabel.h
+
widgets/SmartLabel.h
changedSystemOfMeasurementOrScale(PreviousScaleInfo) popContextMenu(QPoint)
- BtMassEdit + SmartLineEdit QLineEdit -
BtAmountEdit.h
+
widgets/SmartLineEdit.h
textModified() lineChanged() From 7553a465da3834eeb145aad64dd150ea9eead11e Mon Sep 17 00:00:00 2001 From: Matt Young Date: Wed, 5 Apr 2023 21:05:00 +0200 Subject: [PATCH 18/42] SmartLineEdit etc for RefractoDialog --- src/PrimingDialog.cpp | 15 ++---- src/RefractoDialog.cpp | 63 ++++++++++++++--------- src/UiAmountWithUnits.cpp | 6 +++ src/widgets/SmartLineEdit.cpp | 94 +++++++++++++++++++++-------------- src/widgets/SmartLineEdit.h | 86 +++++++++++++++++++++++++------- ui/refractoDialog.ui | 54 ++++---------------- 6 files changed, 187 insertions(+), 131 deletions(-) diff --git a/src/PrimingDialog.cpp b/src/PrimingDialog.cpp index a8dcb07b..ca0b65d4 100644 --- a/src/PrimingDialog.cpp +++ b/src/PrimingDialog.cpp @@ -27,13 +27,6 @@ #include "measurement/Unit.h" -namespace { - auto const BATCH_SIZE = TypeInfo::construct(Measurement::PhysicalQuantity::Volume); - auto const TEMP = TypeInfo::construct(Measurement::PhysicalQuantity::Temperature); - auto const CARB_VOLS = TypeInfo::construct(Measurement::PhysicalQuantity::Carbonation); - auto const SUGAR_AMOUNT = TypeInfo::construct(Measurement::PhysicalQuantity::Mass); -} - PrimingDialog::PrimingDialog(QWidget* parent) : QDialog(parent) { this->setupUi(this); @@ -45,10 +38,10 @@ PrimingDialog::PrimingDialog(QWidget* parent) : QDialog(parent) { this->sugarGroup->addButton(radioButton_sucrose); this->sugarGroup->addButton(radioButton_dme); - SMART_LINE_EDIT_INIT_FS(PrimingDialog, lineEdit_beerVol, BATCH_SIZE , *this->label_beerVol ); - SMART_LINE_EDIT_INIT_FS(PrimingDialog, lineEdit_temp , TEMP , *this->label_temp , 1); - SMART_LINE_EDIT_INIT_FS(PrimingDialog, lineEdit_vols , CARB_VOLS , *this->label_vols , 1); - SMART_LINE_EDIT_INIT_FS(PrimingDialog, lineEdit_output , SUGAR_AMOUNT, *this->label_output ); + SMART_LINE_EDIT_INIT_FS(PrimingDialog, lineEdit_beerVol, double, Measurement::PhysicalQuantity::Volume , *this->label_beerVol ); + SMART_LINE_EDIT_INIT_FS(PrimingDialog, lineEdit_temp , double, Measurement::PhysicalQuantity::Temperature, *this->label_temp , 1); + SMART_LINE_EDIT_INIT_FS(PrimingDialog, lineEdit_vols , double, Measurement::PhysicalQuantity::Carbonation, *this->label_vols , 1); + SMART_LINE_EDIT_INIT_FS(PrimingDialog, lineEdit_output , double, Measurement::PhysicalQuantity::Mass , *this->label_output ); connect(pushButton_calculate, &QAbstractButton::clicked, this, &PrimingDialog::calculate); return; diff --git a/src/RefractoDialog.cpp b/src/RefractoDialog.cpp index 8892c0a9..c9d8c613 100644 --- a/src/RefractoDialog.cpp +++ b/src/RefractoDialog.cpp @@ -32,7 +32,24 @@ RefractoDialog::RefractoDialog(QWidget* parent) : QDialog(parent) { setupUi(this); - connect( pushButton_calculate, &QAbstractButton::clicked, this, &RefractoDialog::calculate ); + SMART_LINE_EDIT_INIT_FS_FIXED(RefractoDialog, lineEdit_op , double, Measurement::PhysicalQuantity::Density , 1); // Original Plato + SMART_LINE_EDIT_INIT_FS_FIXED(RefractoDialog, lineEdit_inputOG, double, Measurement::PhysicalQuantity::Density , 3); // Original gravity in + SMART_LINE_EDIT_INIT_FS_FIXED(RefractoDialog, lineEdit_cp , double, Measurement::PhysicalQuantity::Density , 1); // Current Plato + SMART_LINE_EDIT_INIT_FS_FIXED(RefractoDialog, lineEdit_ri , double, NonPhysicalQuantity::Dimensionless ); // Refractive index + SMART_LINE_EDIT_INIT_FS_FIXED(RefractoDialog, lineEdit_og , double, Measurement::PhysicalQuantity::Density , 3); // Original gravity out + SMART_LINE_EDIT_INIT_FS_FIXED(RefractoDialog, lineEdit_sg , double, Measurement::PhysicalQuantity::Density , 3); // Specific gravity out + SMART_LINE_EDIT_INIT_FS_FIXED(RefractoDialog, lineEdit_abv , double, NonPhysicalQuantity::Percentage ); // Alcohol by volume + SMART_LINE_EDIT_INIT_FS_FIXED(RefractoDialog, lineEdit_abw , double, NonPhysicalQuantity::Percentage ); // Alcohol by weight + SMART_LINE_EDIT_INIT_FS_FIXED(RefractoDialog, lineEdit_re , double, Measurement::PhysicalQuantity::Density , 1); // Real extract Plato + + this->lineEdit_op ->getUiAmountWithUnits().setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::Plato ); + this->lineEdit_inputOG->getUiAmountWithUnits().setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); + this->lineEdit_cp ->getUiAmountWithUnits().setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::Plato ); + this->lineEdit_og ->getUiAmountWithUnits().setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); + this->lineEdit_sg ->getUiAmountWithUnits().setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); + this->lineEdit_re ->getUiAmountWithUnits().setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::Plato ); + + connect(this->pushButton_calculate, &QAbstractButton::clicked, this, &RefractoDialog::calculate ); return; } @@ -49,7 +66,7 @@ void RefractoDialog::calculate() { double inputOG = Measurement::extractRawFromString(lineEdit_inputOG->text(), &haveOG); double currentPlato = Measurement::extractRawFromString(lineEdit_cp ->text(), &haveCP); - clearOutputFields(); + this->clearOutputFields(); // Abort if we don't have the current plato. // I really dislike just doing nothing as the user is POUNDING on the @@ -60,14 +77,14 @@ void RefractoDialog::calculate() { } double ri = Algorithms::refractiveIndex(currentPlato); - lineEdit_ri->setText(Measurement::displayQuantity(ri, 3)); + this->lineEdit_ri->setText(Measurement::displayQuantity(ri, 3)); if (!haveOG && haveOP) { - inputOG = Algorithms::PlatoToSG_20C20C( originalPlato ); - lineEdit_inputOG->setText(inputOG); + inputOG = Algorithms::PlatoToSG_20C20C(originalPlato); + this->lineEdit_inputOG->setAmount(inputOG); } else if (!haveOP && haveOG) { - originalPlato = Algorithms::SG_20C20C_toPlato( inputOG ); - lineEdit_op->setText(inputOG); + originalPlato = Algorithms::SG_20C20C_toPlato(inputOG); + this->lineEdit_op->setAmount(inputOG); } else if (!haveOP && !haveOG) { qDebug() << Q_FUNC_INFO << "no plato or og"; return; // Can't do much if we don't have OG or OP. @@ -81,9 +98,9 @@ void RefractoDialog::calculate() { sg = og; } - double re = Algorithms::realExtract( sg, currentPlato ); - double abv = Algorithms::getABVBySGPlato( sg, currentPlato ); - double abw = Algorithms::getABWBySGPlato( sg, currentPlato ); + double re = Algorithms::realExtract (sg, currentPlato); + double abv = Algorithms::getABVBySGPlato(sg, currentPlato); + double abw = Algorithms::getABWBySGPlato(sg, currentPlato); // Warn the user if the inputOG and calculated og don't match. if( qAbs(og - inputOG) > 0.002 ) { @@ -95,22 +112,22 @@ void RefractoDialog::calculate() { ); } - lineEdit_og->setText(og); - lineEdit_sg->setText(sg); - //Even if the real extract if display in Plato, it must be given in system unit. - //Conversion is made by BtLineEdit - lineEdit_re->setText(Algorithms::PlatoToSG_20C20C(re)); - lineEdit_abv->setText(abv); - lineEdit_abw->setText(abw); + this->lineEdit_og->setAmount(og); + this->lineEdit_sg->setAmount(sg); + // Even if the real extract if display in Plato, it must be given in system unit. + // Conversion is made by SmartLineEdit + this->lineEdit_re ->setAmount(Algorithms::PlatoToSG_20C20C(re)); + this->lineEdit_abv->setAmount(abv); + this->lineEdit_abw->setAmount(abw); return; } void RefractoDialog::clearOutputFields() { - lineEdit_ri->clear(); - lineEdit_og->clear(); - lineEdit_sg->clear(); - lineEdit_re->clear(); - lineEdit_abv->clear(); - lineEdit_abw->clear(); + this->lineEdit_ri ->clear(); + this->lineEdit_og ->clear(); + this->lineEdit_sg ->clear(); + this->lineEdit_re ->clear(); + this->lineEdit_abv->clear(); + this->lineEdit_abw->clear(); return; } diff --git a/src/UiAmountWithUnits.cpp b/src/UiAmountWithUnits.cpp index e48a6bab..aa28706c 100644 --- a/src/UiAmountWithUnits.cpp +++ b/src/UiAmountWithUnits.cpp @@ -134,6 +134,9 @@ void UiAmountWithUnits::selectPhysicalQuantity(Measurement::PhysicalQuantity con } void UiAmountWithUnits::setForcedSystemOfMeasurement(std::optional systemOfMeasurement) { + qDebug() << + Q_FUNC_INFO << "Measurement system" << systemOfMeasurement << "for" << this->pimpl->m_configSection << ">" << + this->pimpl->m_editField; Measurement::setForcedSystemOfMeasurementForField(this->pimpl->m_editField, this->pimpl->m_configSection, systemOfMeasurement); @@ -158,6 +161,9 @@ QString UiAmountWithUnits::getForcedSystemOfMeasurementViaString() const { } void UiAmountWithUnits::setForcedRelativeScale(std::optional relativeScale) { + qDebug() << + Q_FUNC_INFO << "Scale" << relativeScale << "for" << this->pimpl->m_configSection << ">" << + this->pimpl->m_editField; Measurement::setForcedRelativeScaleForField(this->pimpl->m_editField, this->pimpl->m_configSection, relativeScale); return; } diff --git a/src/widgets/SmartLineEdit.cpp b/src/widgets/SmartLineEdit.cpp index 3d3a0d30..5a5b6eac 100644 --- a/src/widgets/SmartLineEdit.cpp +++ b/src/widgets/SmartLineEdit.cpp @@ -50,9 +50,7 @@ class SmartLineEdit::impl { m_uiAmountWithUnits {nullptr}, m_precision {3}, m_maximalDisplayString{"100.000 srm"}, - m_desiredWidthInPixels{0}, - m_editField {""}, - m_configSection {""} { + m_desiredWidthInPixels{0} { return; } @@ -113,7 +111,8 @@ class SmartLineEdit::impl { TypeInfo const & typeInfo, SmartLabel * buddyLabel, std::optional const precision, - QString const & maximalDisplayString) { + QString const & maximalDisplayString, + bool fixedUnitsAndScale = false) { // It's a coding error to call this function twice on the same object, ie we should only initialise something once! Q_ASSERT(!this->m_initialised); @@ -141,20 +140,37 @@ class SmartLineEdit::impl { connect(&this->m_self, &QLineEdit::editingFinished, &this->m_self, &SmartLineEdit::onLineChanged); if (!std::holds_alternative(*this->m_typeInfo->fieldType)) { + + // It's only meaningful to have a UiAmountWithUnits if we are dealing with a PhysicalQuantity. + // It's a coding error if we already created a UiAmountWithUnits + Q_ASSERT(!this->m_uiAmountWithUnits); + this->m_uiAmountWithUnits = + std::make_unique(this->m_self.parentWidget(), + ConvertToPhysicalQuantities(*typeInfo.fieldType)); + // In older versions of the code, we had the .ui file set the "editField" property on a subclass of BtLineEdit + // for the forcedSystemOfMeasurement and/or forcedRelativeScale to be stored in PersistentSettings. Now that + // we have a globally unique name set in init, we use that instead. + this->m_uiAmountWithUnits->setEditField (this->m_name ); + this->m_uiAmountWithUnits->setConfigSection("SmartLineEdit"); + QString configSection = this->m_self.property(*PropertyNames::UiAmountWithUnits::configSection).toString(); qDebug() << Q_FUNC_INFO << "Config Section =" << configSection; this->m_uiAmountWithUnits->setConfigSection(configSection); - // It's a coding error if we didn't specify the buddy for something measuring a physical quantity - Q_ASSERT(this->m_buddyLabel); + // Unless we specified that this is a "fixed" field (ie where the user cannot exercise the choices about units + // and scale that she otherwise would), it's a coding error if we didn't specify the buddy for something + // measuring a physical quantity + Q_ASSERT(fixedUnitsAndScale || this->m_buddyLabel); - // It's also a coding error if we are not the buddy of the label we think we are. However, we cannot test this - // here as the buddy's QLabel::setBuddy() hasn't necessarily yet been called from the code generated from the - // .ui file. What we can do, as belt-and-braces is call it here. - this->m_buddyLabel->setBuddy(&this->m_self); + if (!fixedUnitsAndScale) { + // It's also a coding error if we are not the buddy of the label we think we are. However, we cannot test this + // here as the buddy's QLabel::setBuddy() hasn't necessarily yet been called from the code generated from the + // .ui file. What we can do, as belt-and-braces is call it here. + this->m_buddyLabel->setBuddy(&this->m_self); - connect(this->m_buddyLabel, &SmartLabel::changedSystemOfMeasurementOrScale, &this->m_self, &SmartLineEdit::lineChanged); + connect(this->m_buddyLabel, &SmartLabel::changedSystemOfMeasurementOrScale, &this->m_self, &SmartLineEdit::lineChanged); + } } // We can work out (and store) our display size here, but we don't yet set it. The way the Designer UI Files work is @@ -186,11 +202,6 @@ class SmartLineEdit::impl { unsigned int m_precision; QString m_maximalDisplayString; int m_desiredWidthInPixels; - // .:TBD:. This is a bit ugly. We keep our own copies of fields that exist in UiAmountWithUnits because we get given - // the values before we have created the UiAmountWithUnits object - QString m_editField; - QString m_configSection; - }; SmartLineEdit::SmartLineEdit(QWidget * parent) : QLineEdit(parent), pimpl{std::make_unique(*this)} { @@ -209,15 +220,6 @@ void SmartLineEdit::init(char const * const name, // It's a coding error to call this version of init with a NonPhysicalQuantity Q_ASSERT(typeInfo.fieldType && !std::holds_alternative(*typeInfo.fieldType)); - // It's only meaningful to have a UiAmountWithUnits if we are dealing with a PhysicalQuantity, hence why we do it - // here and not in SmartLineEdit::impl::init(). - // It's a coding error if we already created a UiAmountWithUnits - Q_ASSERT(!this->pimpl->m_uiAmountWithUnits); - this->pimpl->m_uiAmountWithUnits = std::make_unique(this->parentWidget(), ConvertToPhysicalQuantities(*typeInfo.fieldType)); - // See above for why we need to pass these in to UiAmountWithUnits - if (!this->pimpl->m_editField .isEmpty()) { this->pimpl->m_uiAmountWithUnits->setEditField (this->pimpl->m_editField ); } - if (!this->pimpl->m_configSection.isEmpty()) { this->pimpl->m_uiAmountWithUnits->setConfigSection(this->pimpl->m_configSection); } - this->pimpl->init(name, typeInfo, &buddyLabel, precision, maximalDisplayString); return; } @@ -236,6 +238,21 @@ void SmartLineEdit::init(char const * const name, return; } +void SmartLineEdit::initFixed(char const * const name, + TypeInfo const & typeInfo, + std::optional const precision, + QString const & maximalDisplayString) { + qDebug() << Q_FUNC_INFO << name << ":" << typeInfo; + + // Unlike the two init() functions, it's OK to call this function with either a PhysicalQuantity or a + // NonPhysicalQuantity. (Strictly speaking "fixed" is a given for a NonPhysicalQuantity, because there are no units + // or scales to change, but enforcing that as a requirement, would not, I think, help prevent any bugs.) + Q_ASSERT(typeInfo.fieldType); + + this->pimpl->init(name, typeInfo, nullptr, precision, maximalDisplayString, true); + return; +} + BtFieldType const SmartLineEdit::getFieldType() const { Q_ASSERT(this->pimpl->m_initialised); return *this->pimpl->m_typeInfo->fieldType; @@ -287,6 +304,7 @@ template void SmartLineEdit::setAmount(std::optional amount); template void SmartLineEdit::setAmount(T amount) { Q_ASSERT(this->pimpl->m_initialised); + qDebug() << Q_FUNC_INFO << this->pimpl->m_name << "amount =" << amount; if (this->pimpl->m_typeInfo->typeIndex != typeid(T)) { qCritical() << @@ -312,6 +330,10 @@ template void SmartLineEdit::setAmount(T amount) { ); } else { // The field is measuring a physical quantity + qDebug() << + Q_FUNC_INFO << this->pimpl->m_name << "forcedSystemOfMeasurement:" << + this->pimpl->m_uiAmountWithUnits->getForcedSystemOfMeasurement() << ", forcedRelativeScale:" << + this->pimpl->m_uiAmountWithUnits->getForcedRelativeScale(); this->QLineEdit::setText( this->pimpl->m_uiAmountWithUnits->displayAmount(amount, this->pimpl->m_precision) ); @@ -343,17 +365,17 @@ template double SmartLineEdit::getValueAs() const; //============================================ Property Getters and Setters ============================================ // Note that we cannot assume init() has yet been run when these are called from (code generated from) a .ui file - -void SmartLineEdit::setEditField (QString val) { this->pimpl->m_editField = val; if (this->pimpl->m_initialised) { this->pimpl->m_uiAmountWithUnits->setEditField (val); } return; } -void SmartLineEdit::setConfigSection (QString val) { this->pimpl->m_configSection = val; if (this->pimpl->m_initialised) { this->pimpl->m_uiAmountWithUnits->setConfigSection (val); } return; } - // TODO Check where these two are used and whether we can eliminate them -void SmartLineEdit::setForcedSystemOfMeasurementViaString(QString val) { Q_ASSERT(this->pimpl->m_initialised); this->pimpl->m_uiAmountWithUnits->setForcedSystemOfMeasurementViaString(val); return; } -void SmartLineEdit::setForcedRelativeScaleViaString (QString val) { Q_ASSERT(this->pimpl->m_initialised); this->pimpl->m_uiAmountWithUnits->setForcedRelativeScaleViaString (val); return; } - -QString SmartLineEdit::getEditField() const { if (this->pimpl->m_initialised) { return this->pimpl->m_uiAmountWithUnits->getEditField() ; } return this->pimpl->m_editField ; } -QString SmartLineEdit::getConfigSection() /* not const */ { if (this->pimpl->m_initialised) { return this->pimpl->m_uiAmountWithUnits->getConfigSection() /* not const */ ; } return this->pimpl->m_configSection; } -QString SmartLineEdit::getForcedSystemOfMeasurementViaString() const { Q_ASSERT(this->pimpl->m_initialised); return this->pimpl->m_uiAmountWithUnits->getForcedSystemOfMeasurementViaString(); } -QString SmartLineEdit::getForcedRelativeScaleViaString() const { Q_ASSERT(this->pimpl->m_initialised); return this->pimpl->m_uiAmountWithUnits->getForcedRelativeScaleViaString() ; } +/// +///void SmartLineEdit::setEditField (QString val) { this->pimpl->m_editField = val; if (this->pimpl->m_initialised) { this->pimpl->m_uiAmountWithUnits->setEditField (val); } return; } +///void SmartLineEdit::setConfigSection (QString val) { this->pimpl->m_configSection = val; if (this->pimpl->m_initialised) { this->pimpl->m_uiAmountWithUnits->setConfigSection (val); } return; } +/// // TODO Check where these two are used and whether we can eliminate them +///void SmartLineEdit::setForcedSystemOfMeasurementViaString(QString val) { Q_ASSERT(this->pimpl->m_initialised); this->pimpl->m_uiAmountWithUnits->setForcedSystemOfMeasurementViaString(val); return; } +///void SmartLineEdit::setForcedRelativeScaleViaString (QString val) { Q_ASSERT(this->pimpl->m_initialised); this->pimpl->m_uiAmountWithUnits->setForcedRelativeScaleViaString (val); return; } +/// +///QString SmartLineEdit::getEditField() const { if (this->pimpl->m_initialised) { return this->pimpl->m_uiAmountWithUnits->getEditField() ; } return this->pimpl->m_editField ; } +///QString SmartLineEdit::getConfigSection() /* not const */ { if (this->pimpl->m_initialised) { return this->pimpl->m_uiAmountWithUnits->getConfigSection() /* not const */ ; } return this->pimpl->m_configSection; } +///QString SmartLineEdit::getForcedSystemOfMeasurementViaString() const { Q_ASSERT(this->pimpl->m_initialised); return this->pimpl->m_uiAmountWithUnits->getForcedSystemOfMeasurementViaString(); } +///QString SmartLineEdit::getForcedRelativeScaleViaString() const { Q_ASSERT(this->pimpl->m_initialised); return this->pimpl->m_uiAmountWithUnits->getForcedRelativeScaleViaString() ; } //====================================================================================================================== diff --git a/src/widgets/SmartLineEdit.h b/src/widgets/SmartLineEdit.h index 705e19f8..86ed1af4 100644 --- a/src/widgets/SmartLineEdit.h +++ b/src/widgets/SmartLineEdit.h @@ -71,10 +71,10 @@ class SmartLineEdit : public QLineEdit { // relating to physical quantities, the user can individually set the system of measurement and relative scale. // // They all pass through to UiAmountWithUnits - Q_PROPERTY(QString configSection READ getConfigSection WRITE setConfigSection STORED false) - Q_PROPERTY(QString editField READ getEditField WRITE setEditField STORED false) - Q_PROPERTY(QString forcedSystemOfMeasurement READ getForcedSystemOfMeasurementViaString WRITE setForcedSystemOfMeasurementViaString STORED false) - Q_PROPERTY(QString forcedRelativeScale READ getForcedRelativeScaleViaString WRITE setForcedRelativeScaleViaString STORED false) +/// Q_PROPERTY(QString configSection READ getConfigSection WRITE setConfigSection STORED false) +/// Q_PROPERTY(QString editField READ getEditField WRITE setEditField STORED false) +/// Q_PROPERTY(QString forcedSystemOfMeasurement READ getForcedSystemOfMeasurementViaString WRITE setForcedSystemOfMeasurementViaString STORED false) +/// Q_PROPERTY(QString forcedRelativeScale READ getForcedRelativeScaleViaString WRITE setForcedRelativeScaleViaString STORED false) public: @@ -91,8 +91,14 @@ class SmartLineEdit : public QLineEdit { * * Note, in reality, you actually use the \c SMART_LINE_EDIT_INIT macro (see below). * - * \param name Used only for logging, mostly for debugging. (We have hundreds of instances of this object and - * if we detect that one of them is misconfigured, it's very useful to be able to log which one!) + * \param name This should uniquely identify this field in the application. (Usually, it's a combination of the + * owning widget and the member variable, eg "FermentableEditor->lineEdit_color".) This serves two + * purposes: + * - For logging, it helps a lot with debugging. (We have hundreds of instances of this object + * and if we detect that one of them is misconfigured, it's very useful to be able to log which + * one!) + * - For fields where there is a choice of \c SystemOfMeasurement and/or \c RelativeScale, this + * provides a unique name against which to store the user's choice in \c PersistentSettings * * \param typeInfo Tells us what data type we use to store the contents of the field (when converted to * canonical units if it is a \c PhysicalQuantity) and, whether this is an optional @@ -116,6 +122,9 @@ class SmartLineEdit : public QLineEdit { * \brief As above, but for non-physical quantity such as \c NonPhysicalQuantity::Date, * \c NonPhysicalQuantity::String, etc. * + * The reason for having two versions of init() is to make it harder to forget the \c buddyLabel parameter + * when the field relates to a \c PhysicalQuantity. + * * Note, in reality, you actually use the \c SMART_LINE_EDIT_INIT macro (see below). */ void init(char const * const name, @@ -123,6 +132,21 @@ class SmartLineEdit : public QLineEdit { std::optional const precision = std::nullopt, QString const & maximalDisplayString = "100.000 srm"); + /** + * \brief As \c init, but for a \c PhysicalQuantity (or \c Mixed2PhysicalQuantities) field where the user does \b not + * have a choice about units or scales (even though they otherwise would for this sort of + * \c PhysicalQuantity). This is typically used on conversion dialogs, eg \c RefractoDialog, where we are + * asking the user to give us inputs in specific units in order to convert them to other units measuring the + * same physical quantity. + * + * Note that we allow this to be used for a \c NonPhysicalQuantity too because, in this "fixed" case, there is + * no concern about needing to remember whether or not to specify the buddy label. + */ + void initFixed(char const * const name, + TypeInfo const & typeInfo, + std::optional const precision = std::nullopt, + QString const & maximalDisplayString = "100.000 srm"); + BtFieldType const getFieldType() const; TypeInfo const & getTypeInfo() const; @@ -160,15 +184,15 @@ class SmartLineEdit : public QLineEdit { template T getValueAs() const; //========================================== Property Getters and Setters =========================================== - void setEditField(QString editField); - void setConfigSection(QString configSection); - void setForcedSystemOfMeasurementViaString(QString systemOfMeasurementAsString); - void setForcedRelativeScaleViaString(QString relativeScaleAsString); - - QString getEditField() const; - QString getConfigSection(); // This does lazy-loading so isn't const - QString getForcedSystemOfMeasurementViaString() const; - QString getForcedRelativeScaleViaString() const; +/// void setEditField(QString editField); +/// void setConfigSection(QString configSection); +/// void setForcedSystemOfMeasurementViaString(QString systemOfMeasurementAsString); +/// void setForcedRelativeScaleViaString(QString relativeScaleAsString); +/// +/// QString getEditField() const; +/// QString getConfigSection(); // This does lazy-loading so isn't const +/// QString getForcedSystemOfMeasurementViaString() const; +/// QString getForcedRelativeScaleViaString() const; //=================================================================================================================== @@ -245,9 +269,35 @@ public slots: /** *\brief An alternate version of \c SMART_LINE_EDIT_INIT for use when there is no \c modelClass (eg in a free-standing - * calculation dialog that does not update the model). + * calculation dialog that does not update the model). Instead of writing: + * + * static auto const typeInfoFor_lineEdit_temp = TypeInfo::construct(Measurement::PhysicalQuantity::Temperature); + * this->lineEdit_temp->init("PrimingDialog->lineEdit_temp", typeInfoFor_lineEdit_temp, *this->label_temp, 1); + * + * you write: + * + * SMART_LINE_EDIT_INIT_FS(PrimingDialog, lineEdit_temp, double, Measurement::PhysicalQuantity::Temperature, *this->label_temp, 1); + * + * The _FS in the name stands for "free-standing". + * + * \param editorClass As for \c SMART_LINE_EDIT_INIT. + * \param fieldName As for \c SMART_LINE_EDIT_INIT + * \param nativeType The native type in which this value is / would be stored, eg double + * \param btFieldType The \c BtFieldType for this field. Together with \c nativeType, this is used to construct a + * static local \c TypeInfo struct to pass by reference to \c SmartLineEdit::init. + * \param ... Any remaining arguments are passed through to \c SmartLineEdit::init in third position and above + */ +#define SMART_LINE_EDIT_INIT_FS(editorClass, fieldName, nativeType, btFieldType, ...) \ + static auto const typeInfoFor_##fieldName = TypeInfo::construct(btFieldType); \ + this-> fieldName ->init(SLE_LOG_NAME(editorClass, fieldName), typeInfoFor_##fieldName __VA_OPT__(, __VA_ARGS__)) + +/** + * \brief A alternate version of \c SMART_LINE_EDIT_INIT_FS that calls \c SmartLineEdit::initFixed instead of + * \c SmartLineEdit::init. This is what you use for fields where we want to remove the choice of units and scale + * from the user, as explained in comments for \c SmartLineEdit::initFixed. */ -#define SMART_LINE_EDIT_INIT_FS(editorClass, fieldName, typeInfoVar, ...) \ - this-> fieldName ->init(SLE_LOG_NAME(editorClass, fieldName), typeInfoVar __VA_OPT__(, __VA_ARGS__)) +#define SMART_LINE_EDIT_INIT_FS_FIXED(editorClass, fieldName, nativeType, btFieldType, ...) \ + static auto const typeInfoFor_##fieldName = TypeInfo::construct(btFieldType); \ + this-> fieldName ->initFixed(SLE_LOG_NAME(editorClass, fieldName), typeInfoFor_##fieldName __VA_OPT__(, __VA_ARGS__)) #endif diff --git a/ui/refractoDialog.ui b/ui/refractoDialog.ui index fda179ac..cf804288 100644 --- a/ui/refractoDialog.ui +++ b/ui/refractoDialog.ui @@ -38,7 +38,7 @@
- + 128 @@ -51,12 +51,6 @@ Measured original plato - - origPlato - - - Plato - @@ -70,7 +64,7 @@
- + 128 @@ -80,12 +74,6 @@ Measured original gravity - - inputOG - - - SpecificGravity -
@@ -110,7 +98,7 @@
- + 128 @@ -120,12 +108,6 @@ Current measured plato - - curP - - - Plato -
@@ -181,7 +163,7 @@
- + 128 @@ -204,7 +186,7 @@ - + 128 @@ -214,9 +196,6 @@ true - - SpecificGravity - @@ -230,7 +209,7 @@
- + 128 @@ -253,7 +232,7 @@ - + 128 @@ -286,7 +265,7 @@ - + 128 @@ -296,9 +275,6 @@ true - - Plato - @@ -312,7 +288,7 @@
- + 128 @@ -322,9 +298,6 @@ true - - SpecificGravity -
@@ -334,18 +307,13 @@
- BtDensityEdit + SmartLineEdit QLineEdit -
BtAmountEdit.h
+
widgets/SmartLineEdit.h
lineChanged(PreviousScaleInfo)
- - BtGenericEdit - QLineEdit -
BtLineEdit.h
-
lineEdit_op From 9bc894a04861a36c1cbd5ac38abf5979c53f6d52 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Tue, 25 Apr 2023 09:48:29 +0200 Subject: [PATCH 19/42] It compiles, but still some assertions to fix --- meson.build | 15 +- src/AlcoholTool.cpp | 121 ++-- src/BrewDayFormatter.cpp | 60 +- src/BrewDayScrollWidget.cpp | 44 +- src/BrewNoteWidget.cpp | 45 +- src/BtAmountEdit.cpp | 203 ------ src/BtAmountEdit.h | 116 --- src/BtFieldType.h | 4 +- src/BtLabel.cpp | 279 -------- src/BtLabel.h | 167 ----- src/BtLineEdit.cpp | 173 ----- src/BtLineEdit.h | 139 ---- src/BtTreeItem.cpp | 8 +- src/CMakeLists.txt | 9 +- src/EquipmentEditor.cpp | 49 +- src/FermentableDialog.cpp | 56 +- src/FermentableDialog.h | 3 +- src/FermentableEditor.cpp | 24 +- src/FermentableSortFilterProxyModel.cpp | 24 +- src/HopDialog.cpp | 56 +- src/HopDialog.h | 3 +- src/HopEditor.cpp | 48 +- src/HopSortFilterProxyModel.cpp | 51 +- src/HopSortFilterProxyModel.h | 3 +- src/HydrometerTool.cpp | 59 +- src/HydrometerTool.h | 27 +- src/InventoryFormatter.cpp | 20 +- src/MainWindow.cpp | 312 ++++---- src/MainWindow.h | 12 +- src/MashDesigner.cpp | 23 +- src/MashEditor.cpp | 14 +- src/MashStepEditor.cpp | 86 +-- src/MiscDialog.cpp | 92 +-- src/MiscDialog.h | 13 +- src/MiscEditor.cpp | 8 +- src/MiscSortFilterProxyModel.cpp | 11 +- src/NamedMashEditor.cpp | 43 +- src/OgAdjuster.cpp | 36 +- src/OptionDialog.cpp | 3 +- src/PitchDialog.cpp | 64 +- src/PrimingDialog.cpp | 10 +- src/RadarChart.cpp | 15 +- src/RecipeExtrasWidget.cpp | 33 +- src/RecipeFormatter.cpp | 240 ++----- src/RefractoDialog.cpp | 33 +- src/SmartAmounts.cpp | 171 +++++ src/SmartAmounts.h | 272 +++++++ src/SmartField.cpp | 673 ++++++++++++++++++ src/SmartField.h | 279 ++++++++ src/StrikeWaterDialog.cpp | 34 +- src/StyleEditor.cpp | 54 +- src/UiAmountWithUnits.cpp | 332 --------- src/UiAmountWithUnits.h | 185 ----- src/WaterDialog.cpp | 17 +- src/WaterDialog.h | 4 +- src/WaterEditor.cpp | 48 +- src/YeastDialog.cpp | 9 +- src/YeastEditor.cpp | 49 +- src/YeastSortFilterProxyModel.cpp | 11 +- src/measurement/Amount.cpp | 2 +- src/measurement/Amount.h | 2 +- src/measurement/Measurement.cpp | 382 +++++----- src/measurement/Measurement.h | 219 +++--- src/model/BrewNote.cpp | 4 +- src/model/MashStep.cpp | 16 +- src/model/Recipe.cpp | 149 +--- src/model/Recipe.h | 2 +- src/model/Water.cpp | 28 +- src/model/Water.h | 6 +- src/model/Yeast.cpp | 24 +- src/tableModels/BtTableModel.cpp | 101 ++- src/tableModels/BtTableModel.h | 104 ++- src/tableModels/BtTableModelInventory.cpp | 6 +- src/tableModels/BtTableModelInventory.h | 4 +- src/tableModels/FermentableTableModel.cpp | 156 ++-- src/tableModels/FermentableTableModel.h | 17 +- src/tableModels/HopTableModel.cpp | 125 ++-- src/tableModels/HopTableModel.h | 15 +- src/tableModels/MashStepTableModel.cpp | 163 ++--- src/tableModels/MashStepTableModel.h | 16 +- src/tableModels/MiscTableModel.cpp | 147 ++-- src/tableModels/MiscTableModel.h | 18 +- src/tableModels/SaltTableModel.cpp | 133 ++-- src/tableModels/SaltTableModel.h | 18 +- src/tableModels/WaterTableModel.cpp | 89 +-- src/tableModels/WaterTableModel.h | 17 +- src/tableModels/YeastTableModel.cpp | 122 ++-- src/tableModels/YeastTableModel.h | 16 +- src/widgets/BtAmountDigitWidget.cpp | 38 - src/widgets/BtAmountDigitWidget.h | 60 -- ...BtDigitWidget.cpp => SmartDigitWidget.cpp} | 83 ++- .../{BtDigitWidget.h => SmartDigitWidget.h} | 35 +- src/widgets/SmartLabel.cpp | 399 ++++++----- src/widgets/SmartLabel.h | 142 +++- src/widgets/SmartLineEdit.cpp | 361 ++-------- src/widgets/SmartLineEdit.h | 207 +----- translations/bt_ca.ts | 35 +- translations/bt_cs.ts | 37 +- translations/bt_de.ts | 35 +- translations/bt_el.ts | 37 +- translations/bt_en.ts | 46 +- translations/bt_es.ts | 37 +- translations/bt_et.ts | 46 +- translations/bt_eu.ts | 46 +- translations/bt_fr.ts | 35 +- translations/bt_gl.ts | 39 +- translations/bt_hu.ts | 35 +- translations/bt_it.ts | 35 +- translations/bt_lv.ts | 46 +- translations/bt_nb.ts | 39 +- translations/bt_nl.ts | 37 +- translations/bt_pl.ts | 39 +- translations/bt_pt.ts | 37 +- translations/bt_ru.ts | 37 +- translations/bt_sr.ts | 37 +- translations/bt_sv.ts | 35 +- translations/bt_tr.ts | 46 +- translations/bt_zh.ts | 39 +- ui/brewNoteWidget.ui | 20 +- ui/fermentableEditor.ui | 2 +- ui/mainWindow.ui | 98 +-- ui/mashDesigner.ui | 36 +- ui/mashEditor.ui | 2 +- ui/mashStepEditor.ui | 68 +- ui/namedMashEditor.ui | 83 +-- ui/ogAdjuster.ui | 62 +- ui/pitchDialog.ui | 72 +- ui/recipeExtrasWidget.ui | 41 +- ui/strikeWaterDialog.ui | 86 +-- ui/waterDialog.ui | 51 +- ui/waterEditor.ui | 93 ++- ui/yeastEditor.ui | 53 +- 132 files changed, 4658 insertions(+), 5382 deletions(-) delete mode 100644 src/BtAmountEdit.cpp delete mode 100644 src/BtAmountEdit.h delete mode 100644 src/BtLabel.cpp delete mode 100644 src/BtLabel.h delete mode 100644 src/BtLineEdit.cpp delete mode 100644 src/BtLineEdit.h create mode 100644 src/SmartAmounts.cpp create mode 100644 src/SmartAmounts.h create mode 100644 src/SmartField.cpp create mode 100644 src/SmartField.h delete mode 100644 src/UiAmountWithUnits.cpp delete mode 100644 src/UiAmountWithUnits.h delete mode 100644 src/widgets/BtAmountDigitWidget.cpp delete mode 100644 src/widgets/BtAmountDigitWidget.h rename src/widgets/{BtDigitWidget.cpp => SmartDigitWidget.cpp} (72%) rename src/widgets/{BtDigitWidget.h => SmartDigitWidget.h} (67%) diff --git a/meson.build b/meson.build index 9e89a88f..ef88fc47 100644 --- a/meson.build +++ b/meson.build @@ -588,13 +588,10 @@ commonSourceFiles = files([ 'src/BrewDayFormatter.cpp', 'src/BrewDayScrollWidget.cpp', 'src/BrewNoteWidget.cpp', - 'src/BtAmountEdit.cpp', 'src/BtColor.cpp', 'src/BtDatePopup.cpp', 'src/BtFieldType.cpp', 'src/BtFolder.cpp', - 'src/BtLabel.cpp', - 'src/BtLineEdit.cpp', 'src/BtSplashScreen.cpp', 'src/BtTabWidget.cpp', 'src/BtTextEdit.cpp', @@ -693,6 +690,8 @@ commonSourceFiles = files([ 'src/RefractoDialog.cpp', 'src/ScaleRecipeTool.cpp', 'src/SimpleUndoableUpdate.cpp', + 'src/SmartAmounts.cpp', + 'src/SmartField.cpp', 'src/StrikeWaterDialog.cpp', 'src/StyleButton.cpp', 'src/StyleEditor.cpp', @@ -711,7 +710,6 @@ commonSourceFiles = files([ 'src/TimerListDialog.cpp', 'src/TimerMainDialog.cpp', 'src/TimerWidget.cpp', - 'src/UiAmountWithUnits.cpp', 'src/utils/BtException.cpp', 'src/utils/BtStringConst.cpp', 'src/utils/BtStringStream.cpp', @@ -727,9 +725,8 @@ commonSourceFiles = files([ 'src/WaterSortFilterProxyModel.cpp', 'src/WaterTableWidget.cpp', 'src/widgets/Animator.cpp', - 'src/widgets/BtAmountDigitWidget.cpp', - 'src/widgets/BtDigitWidget.cpp', 'src/widgets/SelectionControl.cpp', + 'src/widgets/SmartDigitWidget.cpp', 'src/widgets/SmartLabel.cpp', 'src/widgets/SmartLineEdit.cpp', 'src/widgets/ToggleSwitch.cpp', @@ -771,11 +768,8 @@ mocHeaders = files([ 'src/BrewDayFormatter.h', 'src/BrewDayScrollWidget.h', 'src/BrewNoteWidget.h', - 'src/BtAmountEdit.h', 'src/BtDatePopup.h', 'src/BtFolder.h', - 'src/BtLabel.h', - 'src/BtLineEdit.h', 'src/BtSplashScreen.h', 'src/BtTabWidget.h', 'src/BtTextEdit.h', @@ -863,9 +857,8 @@ mocHeaders = files([ 'src/WaterSortFilterProxyModel.h', 'src/WaterTableWidget.h', 'src/widgets/Animator.h', - 'src/widgets/BtAmountDigitWidget.h', - 'src/widgets/BtDigitWidget.h', 'src/widgets/SelectionControl.h', + 'src/widgets/SmartDigitWidget.h', 'src/widgets/SmartLabel.h', 'src/widgets/SmartLineEdit.h', 'src/widgets/ToggleSwitch.h', diff --git a/src/AlcoholTool.cpp b/src/AlcoholTool.cpp index fa4e7242..5835cd8a 100644 --- a/src/AlcoholTool.cpp +++ b/src/AlcoholTool.cpp @@ -27,8 +27,8 @@ #include #include "Algorithms.h" -#include "BtAmountEdit.h" -#include "BtLineEdit.h" +#include "widgets/SmartLabel.h" +#include "widgets/SmartLineEdit.h" #include "Localization.h" #include "PersistentSettings.h" #include "measurement/SystemOfMeasurement.h" @@ -49,27 +49,34 @@ class AlcoholTool::impl { */ impl(AlcoholTool & self) : self {self}, - label_reading {new QLabel (&self)}, - label_temperature {new QLabel (&self)}, - label_corrected {new QLabel (&self)}, - enableAdvancedInputs {new ToggleSwitch (&self)}, - label_og {new QLabel (&self)}, - input_og {new BtDensityEdit (&self)}, - input_og_temperature {new BtTemperatureEdit(&self)}, - corrected_og {new QLabel (&self)}, - label_fg {new QLabel (&self)}, - input_fg {new BtDensityEdit (&self)}, - input_fg_temperature {new BtTemperatureEdit(&self)}, - corrected_fg {new QLabel (&self)}, - label_calibration_temperature{new QLabel (&self)}, - input_calibration_temperature{new BtTemperatureEdit(&self)}, - label_result {new QLabel (&self)}, - output_result {new QLabel (&self)}, - gridLayout {new QGridLayout (&self)} { + label_reading {new QLabel (&self)}, + label_temperature {new SmartLabel (&self)}, + label_corrected {new QLabel (&self)}, + enableAdvancedInputs {new ToggleSwitch (&self)}, + label_og {new SmartLabel (&self)}, + input_og {new SmartLineEdit(&self)}, + input_og_temperature {new SmartLineEdit(&self)}, + corrected_og {new QLabel (&self)}, + label_fg {new SmartLabel (&self)}, + input_fg {new SmartLineEdit(&self)}, + input_fg_temperature {new SmartLineEdit(&self)}, + corrected_fg {new QLabel (&self)}, + label_calibration_temperature{new SmartLabel (&self)}, + input_calibration_temperature{new SmartLineEdit(&self)}, + label_result {new QLabel (&self)}, + output_result {new QLabel (&self)}, + gridLayout {new QGridLayout (&self)} { this->restoreSettings(); this->enableAdvancedInputs->setFont(QFont("Roboto medium", 13)); this->output_result->setText("%"); this->doLayout(); + + SMART_FIELD_INIT_FS(AlcoholTool, label_og , input_og , double, Measurement::PhysicalQuantity::Density ); + SMART_FIELD_INIT_FS(AlcoholTool, label_fg , input_fg , double, Measurement::PhysicalQuantity::Density ); + SMART_FIELD_INIT_FS(AlcoholTool, label_temperature , input_og_temperature , double, Measurement::PhysicalQuantity::Temperature); + SMART_FIELD_INIT_FS(AlcoholTool, label_temperature , input_fg_temperature , double, Measurement::PhysicalQuantity::Temperature); + SMART_FIELD_INIT_FS(AlcoholTool, label_calibration_temperature, input_calibration_temperature, double, Measurement::PhysicalQuantity::Temperature); + this->connectSignals(); return; } @@ -87,10 +94,10 @@ class AlcoholTool::impl { void doLayout() { this->input_og->setMinimumSize(QSize(80, 0)); - this->input_og->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); +/// this->input_og->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); this->input_fg->setMinimumSize(QSize(80, 0)); - this->input_fg->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); +/// this->input_fg->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); this->label_result->setObjectName(QStringLiteral("label_results")); this->label_result->setContextMenuPolicy(Qt::CustomContextMenu); @@ -189,13 +196,11 @@ class AlcoholTool::impl { void connectSignals() { // If every input field triggers recalculation on modification then we don't need a "Convert" button - for (BtLineEdit const * ii : std::initializer_list{this->input_og, - this->input_fg, - this->input_og_temperature, - this->input_fg_temperature, - this->input_calibration_temperature}) { - connect(ii, &BtLineEdit::textModified, &self, &AlcoholTool::calculate); - } + connect(this->input_og , &SmartLineEdit::textModified, &self, &AlcoholTool::calculate); + connect(this->input_fg , &SmartLineEdit::textModified, &self, &AlcoholTool::calculate); + connect(this->input_og_temperature , &SmartLineEdit::textModified, &self, &AlcoholTool::calculate); + connect(this->input_fg_temperature , &SmartLineEdit::textModified, &self, &AlcoholTool::calculate); + connect(this->input_calibration_temperature, &SmartLineEdit::textModified, &self, &AlcoholTool::calculate); // This will also make the recalculation call after toggling the visibility of advanced controls connect(this->enableAdvancedInputs, &QAbstractButton::clicked, &self, &AlcoholTool::toggleAdvancedControls); @@ -204,13 +209,13 @@ class AlcoholTool::impl { void retranslateUi() { self.setWindowTitle(tr("Alcohol Tool")); - this->label_og->setText(tr("Original Gravity (OG)")); - this->label_result->setText(tr("ABV")); - this->label_fg->setText(tr("Final Gravity (FG)")); - this->label_reading->setText(tr("Reading")); - this->label_temperature->setText(tr("Temperature")); - this->label_corrected->setText(tr("Corrected Reading")); - this->enableAdvancedInputs->setText(tr("Advanced Mode")); + this->label_og ->setText(tr("Original Gravity (OG)")); + this->label_result ->setText(tr("ABV")); + this->label_fg ->setText(tr("Final Gravity (FG)")); + this->label_reading ->setText(tr("Reading")); + this->label_temperature ->setText(tr("Temperature")); + this->label_corrected ->setText(tr("Corrected Reading")); + this->enableAdvancedInputs ->setText(tr("Advanced Mode")); this->label_calibration_temperature->setText(tr("Hydrometer Calibration Temperature")); #ifndef QT_NO_TOOLTIP @@ -238,10 +243,10 @@ class AlcoholTool::impl { // Hydrometer calibration temperature -- default is 20°C, or 68°F in the old money. // Working out which units to use is already solved elsewhere in the code base, but you just have to be careful - // not to do the conversion twice (ie 20°C -> 68°F ... 68°C -> 154°F) as both BtLineEdit::setText() and - // Measurement::amountDisplay() take SI unit and convert them to whatever the user has chosen to display. So you just - // need BtLineEdit::setText(). - this->input_calibration_temperature->setText( + // not to do the conversion twice (ie 20°C -> 68°F ... 68°C -> 154°F) as both SmartLineEdit::setAmount() and + // Measurement::amountDisplay() take SI unit and convert them to whatever the user has chosen to display. So you + // just need SmartLineEdit::setAmount(). + this->input_calibration_temperature->setAmount( PersistentSettings::value(hydrometerCalibrationTemperatureInC, 20.0, PersistentSettings::Sections::alcoholTool).toDouble() @@ -261,25 +266,25 @@ class AlcoholTool::impl { } // Member variables for impl - AlcoholTool & self; - QLabel * label_reading; - QLabel * label_temperature; - QLabel * label_corrected; - ToggleSwitch * enableAdvancedInputs; - QLabel * label_og; - BtDensityEdit * input_og; - BtTemperatureEdit * input_og_temperature; - QLabel * corrected_og; - QLabel * label_fg; - BtDensityEdit * input_fg; - BtTemperatureEdit * input_fg_temperature; - QLabel * corrected_fg; - QLabel * label_calibration_temperature; - BtTemperatureEdit * input_calibration_temperature; - QPushButton * pushButton_convert; - QLabel * label_result; - QLabel * output_result; - QGridLayout * gridLayout; + AlcoholTool & self; + QLabel * label_reading; + SmartLabel * label_temperature; + QLabel * label_corrected; + ToggleSwitch * enableAdvancedInputs; + SmartLabel * label_og; + SmartLineEdit * input_og; + SmartLineEdit * input_og_temperature; + QLabel * corrected_og; + SmartLabel * label_fg; + SmartLineEdit * input_fg; + SmartLineEdit * input_fg_temperature; + QLabel * corrected_fg; + SmartLabel * label_calibration_temperature; + SmartLineEdit * input_calibration_temperature; + QPushButton * pushButton_convert; + QLabel * label_result; + QLabel * output_result; + QGridLayout * gridLayout; }; AlcoholTool::AlcoholTool(QWidget* parent) : QDialog(parent), diff --git a/src/BrewDayFormatter.cpp b/src/BrewDayFormatter.cpp index c3f9fcd1..7cb19609 100644 --- a/src/BrewDayFormatter.cpp +++ b/src/BrewDayFormatter.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * BrewDayFormatter.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * BrewDayFormatter.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Jeff Bailey * • Mattias Måhl * • Matt Young @@ -74,9 +74,7 @@ QString BrewDayFormatter::buildTitleHtml(bool includeImage) { .arg(tr("Boil Time")) .arg( recObs->equipment() ? Measurement::displayAmount(Measurement::Amount{recObs->equipment()->boilTime_min(), - Measurement::Units::minutes}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Equipment::boilTime_min) : "unknown" + Measurement::Units::minutes}) : "unknown" ) .arg(tr("Efficiency")) .arg(Measurement::displayQuantity(recObs->efficiency_pct(), 0)); @@ -84,38 +82,23 @@ QString BrewDayFormatter::buildTitleHtml(bool includeImage) { // third row: pre-Boil Volume and Preboil Gravity body += QString("%1%2%3%4") .arg(tr("Boil Volume")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilVolume_l, - 2)) + .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilVolume_l(), Measurement::Units::liters}, 2)) .arg(tr("Preboil Gravity")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilGrav(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilGrav, - 3)); + .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilGrav(), Measurement::Units::sp_grav}, 3)); // fourth row: Final volume and starting gravity body += QString("%1%2%3%4") .arg(tr("Final Volume")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->finalVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::finalVolume_l, - 2)) + .arg(Measurement::displayAmount(Measurement::Amount{recObs->finalVolume_l(), Measurement::Units::liters}, 2)) .arg(tr("Starting Gravity")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->og(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::og, - 3)); + .arg(Measurement::displayAmount(Measurement::Amount{recObs->og(), Measurement::Units::sp_grav}, 3)); // fifth row: IBU and Final gravity body += QString("%1%2%3%4") .arg(tr("IBU")) .arg(Measurement::displayQuantity(recObs->IBU(), 1)) .arg(tr("Final Gravity")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->fg(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::fg, - 3)); + .arg(Measurement::displayAmount(Measurement::Amount{recObs->fg(), Measurement::Units::sp_grav}, 3)); // sixth row: ABV and estimate calories bool metricVolume = @@ -153,9 +136,7 @@ QList BrewDayFormatter::buildTitleList() { row.append(tr("Boil Time")); row.append( recObs->equipment() ? Measurement::displayAmount(Measurement::Amount{recObs->equipment()->boilTime_min(), - Measurement::Units::minutes}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Equipment::boilTime_min) : "unknown" + Measurement::Units::minutes}) : "unknown" ); row.append(tr("Efficiency")); row.append(Measurement::displayQuantity(recObs->efficiency_pct(), 0)); @@ -164,15 +145,9 @@ QList BrewDayFormatter::buildTitleList() { // third row: pre-Boil Volume and Preboil Gravity row.append(tr("Boil Volume")); - row.append(Measurement::displayAmount(Measurement::Amount{recObs->boilVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilVolume_l, - 2)); + row.append(Measurement::displayAmount(Measurement::Amount{recObs->boilVolume_l(), Measurement::Units::liters}, 2)); row.append(tr("Preboil Gravity")); - row.append(Measurement::displayAmount(Measurement::Amount{recObs->boilGrav(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilGrav, - 3)); + row.append(Measurement::displayAmount(Measurement::Amount{recObs->boilGrav(), Measurement::Units::sp_grav}, 3)); ret.append(row); row.clear(); ret.append(row); @@ -180,15 +155,9 @@ QList BrewDayFormatter::buildTitleList() { // fourth row: Final volume and starting gravity row.append(tr("Final Volume")); - row.append(Measurement::displayAmount(Measurement::Amount{recObs->finalVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::finalVolume_l, - 2)); + row.append(Measurement::displayAmount(Measurement::Amount{recObs->finalVolume_l(), Measurement::Units::liters}, 2)); row.append(tr("Starting Gravity")); - row.append(Measurement::displayAmount(Measurement::Amount{recObs->og(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::og, - 3)); + row.append(Measurement::displayAmount(Measurement::Amount{recObs->og(), Measurement::Units::sp_grav}, 3)); ret.append(row); row.clear(); @@ -196,10 +165,7 @@ QList BrewDayFormatter::buildTitleList() { row.append(tr("IBU")); row.append(Measurement::displayQuantity(recObs->IBU(), 1)); row.append(tr("Final Gravity")); - row.append(Measurement::displayAmount(Measurement::Amount{recObs->fg(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::fg, - 3)); + row.append(Measurement::displayAmount(Measurement::Amount{recObs->fg(), Measurement::Units::sp_grav}, 3)); ret.append(row); row.clear(); diff --git a/src/BrewDayScrollWidget.cpp b/src/BrewDayScrollWidget.cpp index 9fb71420..55082ab0 100644 --- a/src/BrewDayScrollWidget.cpp +++ b/src/BrewDayScrollWidget.cpp @@ -54,9 +54,7 @@ namespace { return "unknown"; } - return Measurement::displayAmount(Measurement::Amount{equipment->boilTime_min(), Measurement::Units::minutes}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilTime_min); + return Measurement::displayAmount(Measurement::Amount{equipment->boilTime_min(), Measurement::Units::minutes}); } } @@ -66,14 +64,13 @@ BrewDayScrollWidget::BrewDayScrollWidget(QWidget* parent) : QWidget{parent}, this->setupUi(this); this->setObjectName("BrewDayScrollWidget"); - connect(listWidget, &QListWidget::currentRowChanged, this, &BrewDayScrollWidget::showInstruction ); - connect(btTextEdit, SIGNAL(textModified()), this, SLOT(saveInstruction()) ); -// connect(btTextEdit, &BtLineEdit::textModified, this, &BrewDayScrollWidget::saveInstruction ); - connect(pushButton_insert, &QAbstractButton::clicked, this, &BrewDayScrollWidget::insertInstruction ); - connect(pushButton_remove, &QAbstractButton::clicked, this, &BrewDayScrollWidget::removeSelectedInstruction); - connect(pushButton_up, &QAbstractButton::clicked, this, &BrewDayScrollWidget::pushInstructionUp ); - connect(pushButton_down, &QAbstractButton::clicked, this, &BrewDayScrollWidget::pushInstructionDown ); - connect(pushButton_generateInstructions, &QAbstractButton::clicked, this, &BrewDayScrollWidget::generateInstructions ); + connect(this->listWidget, &QListWidget::currentRowChanged, this, &BrewDayScrollWidget::showInstruction ); + connect(this->btTextEdit, &BtTextEdit::textModified, this, &BrewDayScrollWidget::saveInstruction ); + connect(this->pushButton_insert, &QAbstractButton::clicked, this, &BrewDayScrollWidget::insertInstruction ); + connect(this->pushButton_remove, &QAbstractButton::clicked, this, &BrewDayScrollWidget::removeSelectedInstruction); + connect(this->pushButton_up, &QAbstractButton::clicked, this, &BrewDayScrollWidget::pushInstructionUp ); + connect(this->pushButton_down, &QAbstractButton::clicked, this, &BrewDayScrollWidget::pushInstructionDown ); + connect(this->pushButton_generateInstructions, &QAbstractButton::clicked, this, &BrewDayScrollWidget::generateInstructions ); return; } @@ -377,38 +374,23 @@ QString BrewDayScrollWidget::buildTitleTable(bool includeImage) { // third row: pre-Boil Volume and Preboil Gravity body += QString("%1%2%3%4") .arg(tr("Boil Volume")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilVolume_l, - 2)) + .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilVolume_l(), Measurement::Units::liters}, 2)) .arg(tr("Preboil Gravity")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilGrav(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::og, - 3)); + .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilGrav(), Measurement::Units::sp_grav}, 3)); // fourth row: Final volume and starting gravity body += QString("%1%2%3%4") .arg(tr("Final Volume")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->finalVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::finalVolume_l, - 2)) + .arg(Measurement::displayAmount(Measurement::Amount{recObs->finalVolume_l(), Measurement::Units::liters}, 2)) .arg(tr("Starting Gravity")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->og(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::og, - 3)); + .arg(Measurement::displayAmount(Measurement::Amount{recObs->og(), Measurement::Units::sp_grav}, 3)); // fifth row: IBU and Final gravity body += QString("%1%2%3%4") .arg(tr("IBU")) .arg( Measurement::displayQuantity(recObs->IBU(), 1)) .arg(tr("Final Gravity")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->fg(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::fg, - 3)); + .arg(Measurement::displayAmount(Measurement::Amount{recObs->fg(), Measurement::Units::sp_grav}, 3)); // sixth row: ABV and estimate calories bool metricVolume = ( diff --git a/src/BrewNoteWidget.cpp b/src/BrewNoteWidget.cpp index ec2fc2cc..cc312514 100644 --- a/src/BrewNoteWidget.cpp +++ b/src/BrewNoteWidget.cpp @@ -38,16 +38,25 @@ BrewNoteWidget::BrewNoteWidget(QWidget *parent) : QWidget(parent) { bNoteObs = 0; setObjectName("BrewNoteWidget"); - SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_Fg , PropertyNames::BrewNote::fg , *this->label_Fg ); - SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_Og , PropertyNames::BrewNote::og , *this->label_Og ); - SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_Sg , PropertyNames::BrewNote::sg , *this->label_Sg ); - SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_mashFinTemp, PropertyNames::BrewNote::mashFinTemp_c , *this->label_mashFinTemp); - SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_pitchTemp , PropertyNames::BrewNote::pitchTemp_c , *this->label_pitchTemp ); - SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_strikeTemp , PropertyNames::BrewNote::strikeTemp_c , *this->label_strikeTemp ); - SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_finalVolume, PropertyNames::BrewNote::finalVolume_l , *this->label_finalVolume); - SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_postBoilVol, PropertyNames::BrewNote::postBoilVolume_l, *this->label_postBoilVol); - SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_volIntoBk , PropertyNames::BrewNote::volumeIntoBK_l , *this->label_volIntoBk ); - SMART_LINE_EDIT_INIT(BrewNoteWidget, BrewNote, lineEdit_volIntoFerm, PropertyNames::BrewNote::volumeIntoFerm_l, *this->label_volIntoFerm); + SMART_FIELD_INIT(BrewNoteWidget, label_Fg , lineEdit_Fg , BrewNote, PropertyNames::BrewNote::fg ); + SMART_FIELD_INIT(BrewNoteWidget, label_Og , lineEdit_Og , BrewNote, PropertyNames::BrewNote::og ); + SMART_FIELD_INIT(BrewNoteWidget, label_Sg , lineEdit_Sg , BrewNote, PropertyNames::BrewNote::sg ); + SMART_FIELD_INIT(BrewNoteWidget, label_mashFinTemp, lineEdit_mashFinTemp , BrewNote, PropertyNames::BrewNote::mashFinTemp_c ); + SMART_FIELD_INIT(BrewNoteWidget, label_pitchTemp , lineEdit_pitchTemp , BrewNote, PropertyNames::BrewNote::pitchTemp_c ); + SMART_FIELD_INIT(BrewNoteWidget, label_strikeTemp , lineEdit_strikeTemp , BrewNote, PropertyNames::BrewNote::strikeTemp_c ); + SMART_FIELD_INIT(BrewNoteWidget, label_finalVolume, lineEdit_finalVolume , BrewNote, PropertyNames::BrewNote::finalVolume_l ); + SMART_FIELD_INIT(BrewNoteWidget, label_postBoilVol, lineEdit_postBoilVol , BrewNote, PropertyNames::BrewNote::postBoilVolume_l); + SMART_FIELD_INIT(BrewNoteWidget, label_volIntoBk , lineEdit_volIntoBk , BrewNote, PropertyNames::BrewNote::volumeIntoBK_l ); + SMART_FIELD_INIT(BrewNoteWidget, label_volIntoFerm, lineEdit_volIntoFerm , BrewNote, PropertyNames::BrewNote::volumeIntoFerm_l); + SMART_FIELD_INIT(BrewNoteWidget, label_projectedOg, lcdnumber_projectedOG, BrewNote, PropertyNames::BrewNote::projOg ); + +// SMART_FIELD_INIT(BrewNoteWidget, label_fermentDate , lineEdit_fermentDate , BrewNote, PropertyNames::BrewNote::fermentDate ); No specialisation for QDateTimeEdit + SMART_FIELD_INIT(BrewNoteWidget, label_effInfoBk , lcdnumber_effBK , BrewNote, PropertyNames::BrewNote::effIntoBK_pct ); + SMART_FIELD_INIT(BrewNoteWidget, label_brewHouseEff , lcdnumber_brewhouseEff, BrewNote, PropertyNames::BrewNote::brewhouseEff_pct); + SMART_FIELD_INIT(BrewNoteWidget, label_projectedAbv , lcdnumber_projABV , BrewNote, PropertyNames::BrewNote::projABV_pct ); + SMART_FIELD_INIT(BrewNoteWidget, label_Abv , lcdnumber_abv , BrewNote, PropertyNames::BrewNote::abv ); + SMART_FIELD_INIT(BrewNoteWidget, label_yeastProjAtten, lcdnumber_projAtten , BrewNote, PropertyNames::BrewNote::projAtten ); + SMART_FIELD_INIT(BrewNoteWidget, label_yeastAtten , lcdnumber_atten , BrewNote, PropertyNames::BrewNote::attenuation ); connect(this->lineEdit_Sg, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateSG ); connect(this->lineEdit_volIntoBk, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateVolumeIntoBK_l ); @@ -59,13 +68,12 @@ BrewNoteWidget::BrewNoteWidget(QWidget *parent) : QWidget(parent) { connect(this->lineEdit_pitchTemp, &SmartLineEdit::textModified, this, &BrewNoteWidget::updatePitchTemp_c ); connect(this->lineEdit_Fg, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateFG ); connect(this->lineEdit_finalVolume, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateFinalVolume_l ); - connect(this->lineEdit_fermentDate, &QDateTimeEdit::dateChanged, this, &BrewNoteWidget::updateFermentDate ); - connect(this->btTextEdit_brewNotes, &BtTextEdit::textModified, this, &BrewNoteWidget::updateNotes ); + connect(this->lineEdit_fermentDate, &QDateTimeEdit::dateChanged, this, &BrewNoteWidget::updateFermentDate ); + connect(this->btTextEdit_brewNotes, &BtTextEdit::textModified, this, &BrewNoteWidget::updateNotes ); // A few labels on this page need special handling, so I connect them here // instead of how we would normally do this. connect(this->label_projectedOg, &SmartLabel::changedSystemOfMeasurementOrScale, this, &BrewNoteWidget::updateProjOg); -/// connect(btLabel_fermentDate, &BtLabel::changedSystemOfMeasurementOrScale, this, &BrewNoteWidget::updateDateFormat); // I think this might work updateDateFormat(); @@ -90,18 +98,13 @@ void BrewNoteWidget::updateDateFormat() { void BrewNoteWidget::updateProjOg() { // Density UnitSystems only have one scale, so we don't bother looking up UnitSystem::RelativeScale - auto forcedSystemOfMeasurement = - Measurement::getForcedSystemOfMeasurementForField(*PropertyNames::BrewNote::projOg, - *PersistentSettings::Sections::page_preboil); + auto forcedSystemOfMeasurement = this->label_projectedOg->getForcedSystemOfMeasurement(); double quant = Measurement::amountDisplay(Measurement::Amount{this->bNoteObs->projOg(), Measurement::Units::sp_grav}, forcedSystemOfMeasurement); this->lcdnumber_projectedOG->setLowLim( lowLimitPct * quant); this->lcdnumber_projectedOG->setHighLim(highLimitPct * quant); - Measurement::UnitSystem const & displayUnitSystem = - Measurement::getUnitSystemForField(*PropertyNames::BrewNote::projOg, - *PersistentSettings::Sections::page_preboil, - Measurement::PhysicalQuantity::Density); + Measurement::UnitSystem const & displayUnitSystem = this->label_projectedOg->getDisplayUnitSystem(); int precision = (displayUnitSystem == Measurement::UnitSystems::density_Plato) ? 0 : 3; this->lcdnumber_projectedOG->display(quant, precision); @@ -158,7 +161,7 @@ void BrewNoteWidget::updatePostBoilVolume_l() { if (this->bNote void BrewNoteWidget::updateVolumeIntoFerm_l() { if (this->bNoteObs) { this->bNoteObs->setVolumeIntoFerm_l(this->lineEdit_volIntoFerm->toCanonical().quantity()); this->showChanges(); } return; } void BrewNoteWidget::updatePitchTemp_c() { if (this->bNoteObs) { this->bNoteObs->setPitchTemp_c (this->lineEdit_pitchTemp ->toCanonical().quantity()); this->showChanges(); } return; } void BrewNoteWidget::updateFG() { if (this->bNoteObs) { this->bNoteObs->setFg (this->lineEdit_Fg ->toCanonical().quantity()); this->showChanges(); } return; } -void BrewNoteWidget::updateFinalVolume_l() { if (this->bNoteObs) { this->bNoteObs->setFinalVolume_l (this->lineEdit_finalVolume ->toCanonical().quantity()); } return; } +void BrewNoteWidget::updateFinalVolume_l() { if (this->bNoteObs) { this->bNoteObs->setFinalVolume_l (this->lineEdit_finalVolume->toCanonical().quantity()); } return; } void BrewNoteWidget::updateFermentDate(QDate const & datetime) { if (this->bNoteObs) { this->bNoteObs->setFermentDate (datetime); } return; } void BrewNoteWidget::updateNotes() { if (this->bNoteObs) { this->bNoteObs->setNotes (this->btTextEdit_brewNotes->toPlainText() ); } return; } diff --git a/src/BtAmountEdit.cpp b/src/BtAmountEdit.cpp deleted file mode 100644 index 01c53ba1..00000000 --- a/src/BtAmountEdit.cpp +++ /dev/null @@ -1,203 +0,0 @@ -/*====================================================================================================================== - * BtAmountEdit.cpp is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Mark de Wever - * • Mattias Måhl - * • Matt Young - * • Mike Evans - * • Mik Firestone - * • Philip Greggory Lee - * • Théophane Martin - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "BtAmountEdit.h" - -#include - -#include "Localization.h" -#include "measurement/Measurement.h" -#include "model/NamedEntity.h" -#include "utils/OptionalHelpers.h" - -BtAmountEdit::BtAmountEdit(QWidget *parent, - Measurement::PhysicalQuantities const physicalQuantities, - int const defaultPrecision, - QString const & maximalDisplayString) : - BtLineEdit{parent, - ConvertToBtFieldType(physicalQuantities), - defaultPrecision, - maximalDisplayString}, - UiAmountWithUnits{parent, physicalQuantities} { - this->setConfigSection(this->property("configSection").toString()); - - connect(this, &QLineEdit::editingFinished, this, &BtAmountEdit::onLineChanged); - - return; -} - -BtAmountEdit::~BtAmountEdit() = default; - -Measurement::Amount BtAmountEdit::toCanonical() const { - return this->rawToCanonical(this->text()); -} - -void BtAmountEdit::setText(double amount) { - this->setText(amount, this->defaultPrecision); - return; -} - -void BtAmountEdit::setText(double amount, int precision) { - this->QLineEdit::setText(this->displayAmount(amount, precision)); - this->setDisplaySize(); - return; -} - -void BtAmountEdit::setText(NamedEntity * element) { - this->setText(element, this->defaultPrecision); - return; -} - -void BtAmountEdit::setText(NamedEntity * element, int precision) { - QString const propertyName = this->getEditField(); - QVariant const propertyValue = element->property(propertyName.toLatin1().constData()); - qDebug() << - Q_FUNC_INFO << "Read property" << propertyName << "(" << propertyName << ") of" << *element << "as" << - propertyValue; - - bool force = false; - QString display; - if (propertyValue.canConvert(QVariant::Double)) { - bool ok = false; - // It is important here to use QVariant::toDouble() instead of going - // through toString() and then Localization::toDouble(). - double amount = propertyValue.toDouble(&ok); - if (!ok) { - qWarning() << - Q_FUNC_INFO << "Could not convert " << propertyValue.toString() << " (" << this->getConfigSection() << ":" << - propertyName << ") to double"; - } - - display = this->displayAmount(amount, precision); - } else { - display = "?"; - } - - this->QLineEdit::setText(display); - this->setDisplaySize(force); - return; -} - -void BtAmountEdit::setText(QString amount) { - this->setText(amount, this->defaultPrecision); - return; -} - -void BtAmountEdit::setText(QString amount, int precision) { - bool ok = false; - double amt = Localization::toDouble(amount, &ok); - if (!ok) { - qWarning() << - Q_FUNC_INFO << "Could not convert" << amount << "(" << this->getConfigSection() << ":" << this->getEditField() << - ") to double"; - } - this->QLineEdit::setText(this->displayAmount(amt, precision)); - - this->setDisplaySize(false); - return; -} - -void BtAmountEdit::setText(QVariant amount) { - this->setText(amount, this->defaultPrecision); - return; -} - -void BtAmountEdit::setText(QVariant amount, int precision) { - this->setText(amount.toString(), precision); - return; -} - -void BtAmountEdit::onLineChanged() { - auto const myFieldType = this->getFieldType(); - qDebug() << - Q_FUNC_INFO << "this->fieldType=" << myFieldType << ", this->m_canonicalUnits=" << this->m_canonicalUnits << - ", forcedSystemOfMeasurement=" << this->getForcedSystemOfMeasurement() << ", forcedRelativeScale=" << - this->getForcedRelativeScale() << ", value=" << this->text(); - - Measurement::PhysicalQuantities const physicalQuantities = ConvertToPhysicalQuantities(myFieldType); - - QString const propertyName = this->getEditField(); - QString const configSection = this->getConfigSection(); - Measurement::SystemOfMeasurement const oldSystemOfMeasurement = - Measurement::getSystemOfMeasurementForField(propertyName, configSection, physicalQuantities); - auto oldForcedRelativeScale = Measurement::getForcedRelativeScaleForField(propertyName, configSection); - PreviousScaleInfo previousScaleInfo{ - oldSystemOfMeasurement, - oldForcedRelativeScale - }; - - qDebug() << - Q_FUNC_INFO << "oldSystemOfMeasurement=" << oldSystemOfMeasurement << ", oldForcedRelativeScale=" << - oldForcedRelativeScale; - - this->lineChanged(previousScaleInfo); - return; -} - -void BtAmountEdit::lineChanged(PreviousScaleInfo previousScaleInfo) { - // editingFinished happens on focus being lost, regardless of anything - // being changed. I am hoping this short circuits properly and we do - // nothing if nothing changed. - if (this->sender() == this && !isModified()) { - qDebug() << Q_FUNC_INFO << "Nothing changed; field holds" << this->text(); - return; - } - - this->QLineEdit::setText(this->correctEnteredText(this->text(), 3, previousScaleInfo)); - - if (sender() == this) { - emit textModified(); - } - - return; -} - -BtMassEdit ::BtMassEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Mass ) { return; } -BtVolumeEdit ::BtVolumeEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Volume ) { return; } -BtTimeEdit ::BtTimeEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Time , 3) { return; } -BtTemperatureEdit ::BtTemperatureEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Temperature , 1) { return; } -BtColorEdit ::BtColorEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Color ) { return; } -BtDensityEdit ::BtDensityEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Density ) { return; } -BtDiastaticPowerEdit ::BtDiastaticPowerEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::DiastaticPower ) { return; } -BtAcidityEdit ::BtAcidityEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Acidity ) { return; } -BtBitternessEdit ::BtBitternessEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Bitterness ) { return; } -BtCarbonationEdit ::BtCarbonationEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Carbonation ) { return; } -BtMassConcentrationEdit ::BtMassConcentrationEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::MassConcentration ) { return; } -BtVolumeConcentrationEdit ::BtVolumeConcentrationEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::VolumeConcentration ) { return; } -BtViscosityEdit ::BtViscosityEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Viscosity ) { return; } -BtSpecificHeatCapacityEdit::BtSpecificHeatCapacityEdit(QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::SpecificHeatCapacity ) { return; } -BtMixedMassOrVolumeEdit ::BtMixedMassOrVolumeEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PqEitherMassOrVolume ) { return; } - -void BtMixedMassOrVolumeEdit::setIsWeight(bool state) { - qDebug() << Q_FUNC_INFO << "state is" << state; - // But you have to admit, this is clever - this->selectPhysicalQuantity(state ? Measurement::PhysicalQuantity::Mass : Measurement::PhysicalQuantity::Volume); - - // maybe? My head hurts now - this->onLineChanged(); - - // Strictly, if we change a Misc to be measured by mass instead of volume (or vice versa) we should also somehow tell - // any other bit of the UI that is showing that Misc (eg a RecipeEditor or MainWindow) to redisplay the relevant - // field. Currently we don't do this, on the assumption that it's rare you will change how a Misc is measured after - // you started using it in recipes. - return; -} diff --git a/src/BtAmountEdit.h b/src/BtAmountEdit.h deleted file mode 100644 index 50c471c1..00000000 --- a/src/BtAmountEdit.h +++ /dev/null @@ -1,116 +0,0 @@ -/*====================================================================================================================== - * BtAmountEdit.h is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Mark de Wever - * • Matt Young - * • Mike Evans - * • Mik Firestone - * • Philip Greggory Lee - * • Scott Peshak - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#ifndef BTAMOUNTEDIT_H -#define BTAMOUNTEDIT_H -#pragma once - -#include "BtLineEdit.h" -#include "measurement/PhysicalQuantity.h" -#include "measurement/Unit.h" -#include "measurement/UnitSystem.h" -#include "UiAmountWithUnits.h" - -/** - * \brief Extends \c BtLineEdit for any numerical field where the user has a choice of units. Handles all the unit - * transformations. - * - * NB: Per https://doc.qt.io/qt-5/moc.html#multiple-inheritance-requires-qobject-to-be-first, "If you are using - * multiple inheritance, moc [Qt's Meta-Object Compiler] assumes that the first inherited class is a subclass of - * QObject. Also, be sure that only the first inherited class is a QObject." In particular, this means we must - * put Q_PROPERTY declarations for UiAmountWithUnits attributes here rather than in UiAmountWithUnits itself. - */ -class BtAmountEdit : public BtLineEdit, public UiAmountWithUnits { - Q_OBJECT - - Q_PROPERTY(QString configSection READ getConfigSection WRITE setConfigSection STORED false) - Q_PROPERTY(QString editField READ getEditField WRITE setEditField STORED false) - Q_PROPERTY(QString forcedSystemOfMeasurement READ getForcedSystemOfMeasurementViaString WRITE setForcedSystemOfMeasurementViaString STORED false) - Q_PROPERTY(QString forcedRelativeScale READ getForcedRelativeScaleViaString WRITE setForcedRelativeScaleViaString STORED false) - -public: - BtAmountEdit(QWidget * parent, - Measurement::PhysicalQuantities const physicalQuantities, - int const defaultPrecision = 3, - QString const & maximalDisplayString = "100.000 srm"); - virtual ~BtAmountEdit(); - -/// /** -/// * \see \c UiAmountWithUnits for what this member function needs to do -/// */ -/// virtual QString getWidgetText() const; -/// -/// /** -/// * \see \c UiAmountWithUnits for what this member function needs to do -/// */ -/// virtual void setWidgetText(QString text); - - Measurement::Amount toCanonical() const; - - // Use one of these when you just want to set the text - void setText(NamedEntity* element); - void setText(NamedEntity* element, int precision); - void setText(double amount); - void setText(double amount, int precision); - void setText(QString amount); - void setText(QString amount, int precision); - void setText(QVariant amount); - void setText(QVariant amount, int precision); - -public slots: - void onLineChanged(); - - /** - * \brief Received from \c BtLabel when the user has change \c UnitSystem - * - * This is mostly referenced in .ui files. (NB this means that the signal connections are only checked at run-time.) - */ - void lineChanged(PreviousScaleInfo previousScaleInfo); -}; - -// -// See comment in BtLineEdit.h for why we need all these trivial child classes to use in .ui files -// -class BtMassEdit : public BtAmountEdit { Q_OBJECT public: BtMassEdit (QWidget* parent); }; -class BtVolumeEdit : public BtAmountEdit { Q_OBJECT public: BtVolumeEdit (QWidget* parent); }; -class BtTimeEdit : public BtAmountEdit { Q_OBJECT public: BtTimeEdit (QWidget* parent); }; -class BtTemperatureEdit : public BtAmountEdit { Q_OBJECT public: BtTemperatureEdit (QWidget* parent); }; -class BtColorEdit : public BtAmountEdit { Q_OBJECT public: BtColorEdit (QWidget* parent); }; -class BtDensityEdit : public BtAmountEdit { Q_OBJECT public: BtDensityEdit (QWidget* parent); }; -class BtDiastaticPowerEdit : public BtAmountEdit { Q_OBJECT public: BtDiastaticPowerEdit (QWidget* parent); }; -class BtAcidityEdit : public BtAmountEdit { Q_OBJECT public: BtAcidityEdit (QWidget* parent); }; -class BtBitternessEdit : public BtAmountEdit { Q_OBJECT public: BtBitternessEdit (QWidget* parent); }; -class BtCarbonationEdit : public BtAmountEdit { Q_OBJECT public: BtCarbonationEdit (QWidget* parent); }; -class BtMassConcentrationEdit : public BtAmountEdit { Q_OBJECT public: BtMassConcentrationEdit (QWidget* parent); }; -class BtVolumeConcentrationEdit : public BtAmountEdit { Q_OBJECT public: BtVolumeConcentrationEdit (QWidget* parent); }; -class BtViscosityEdit : public BtAmountEdit { Q_OBJECT public: BtViscosityEdit (QWidget* parent); }; -class BtSpecificHeatCapacityEdit : public BtAmountEdit { Q_OBJECT public: BtSpecificHeatCapacityEdit(QWidget* parent); }; - -// So-called "mixed" objects, ie ones where we accept two different types of measurement (eg Mass and Volume) are a pain -class BtMixedMassOrVolumeEdit : public BtAmountEdit { - Q_OBJECT -public: - BtMixedMassOrVolumeEdit(QWidget* parent); -public slots: - void setIsWeight(bool state); -}; - -#endif diff --git a/src/BtFieldType.h b/src/BtFieldType.h index 2ab6b4b4..ea4a37b9 100644 --- a/src/BtFieldType.h +++ b/src/BtFieldType.h @@ -36,8 +36,8 @@ enum class NonPhysicalQuantity { Bool, /** * \brief This is for a number that has no units, not even pseudo ones. It is currently a bit over-used -- ie there - * are places we are using this (typically via BtNumberOnlyEdit) where we probably should be using a - * \c PhysicalQuantity. We should fix these over time. + * are places we are using this where we probably should be using a \c PhysicalQuantity. We should fix these + * over time. */ Dimensionless, }; diff --git a/src/BtLabel.cpp b/src/BtLabel.cpp deleted file mode 100644 index a68e8bae..00000000 --- a/src/BtLabel.cpp +++ /dev/null @@ -1,279 +0,0 @@ -/*====================================================================================================================== - * BtLabel.cpp is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Mark de Wever - * • Matt Young - * • Mik Firestone - * • Philip Greggory Lee - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "BtLabel.h" - -#include -#include -#include - -#include "BtAmountEdit.h" -#include "measurement/Measurement.h" -#include "model/Style.h" -#include "model/Recipe.h" -#include "PersistentSettings.h" -#include "utils/OptionalHelpers.h" -#include "widgets/UnitAndScalePopUpMenu.h" - -BtLabel::BtLabel(QWidget *parent, - BtFieldType fieldType) : - QLabel{parent}, - fieldType{fieldType}, - btParent{parent}, - contextMenu{nullptr} { - connect(this, &QWidget::customContextMenuRequested, this, &BtLabel::popContextMenu); - return; -} - -BtLabel::~BtLabel() = default; - -BtLineEdit & BtLabel::getBuddy() const { - // Call QLabel's built-in function to get the buddy - QWidget * buddy = this->buddy(); - - // We assert that it's a coding error for there not to be a buddy! - Q_ASSERT(buddy); - - return static_cast(*buddy); -} - -void BtLabel::enterEvent([[maybe_unused]] QEvent * event) { - this->textEffect(true); - return; -} - -void BtLabel::leaveEvent([[maybe_unused]] QEvent * event) { - this->textEffect(false); - return; -} - -void BtLabel::mouseReleaseEvent(QMouseEvent * event) { - // For the moment, we want left-click and right-click to have the same effect, so when we get a left-click event, we - // send ourselves the right-click signal, which will then fire BtLabel::popContextMenu(). - emit this->QWidget::customContextMenuRequested(event->pos()); - return; -} - -void BtLabel::textEffect(bool enabled) { - QFont myFont = this->font(); - myFont.setUnderline(enabled); - this->setFont(myFont); - return; -} - -void BtLabel::initializeSection() { - if (!this->configSection.isEmpty()) { - return; - } - - // as much as I dislike it, dynamic properties can't be referenced on - // initialization. - QWidget * mybuddy = this->buddy(); - - // - // If the label has the configSection defined, use it - // otherwise, if the paired field has a configSection, use it - // otherwise, if the parent object has a configSection, use it - // if all else fails, get the parent's object name - // - if (this->property("configSection").isValid()) { - this->configSection = property("configSection").toString(); - } else if (mybuddy && mybuddy->property("configSection").isValid() ) { - this->configSection = mybuddy->property("configSection").toString(); - } else if (this->btParent->property("configSection").isValid() ) { - this->configSection = this->btParent->property("configSection").toString(); - } else { - qWarning() << Q_FUNC_INFO << "this failed" << this; - this->configSection = this->btParent->objectName(); - } - return; -} - -void BtLabel::initializeProperty() { - - if (!this->propertyName.isEmpty()) { - return; - } - - QWidget* mybuddy = this->buddy(); - if (this->property("editField").isValid()) { - this->propertyName = this->property("editField").toString(); - } else if (mybuddy && mybuddy->property("editField").isValid()) { - this->propertyName = mybuddy->property("editField").toString(); - } else { - qWarning() << Q_FUNC_INFO << "That failed miserably"; - } - return; -} - -void BtLabel::initializeMenu() { - // If a context menu already exists, we need to delete it and recreate it. We can't always reuse an existing menu - // because the sub-menu for relative scale needs to change when a different unit system is selected. (In theory we - // could only recreate the context menu when a different unit system is selected, but that adds complication.) - if (this->contextMenu) { - // NB: Although the existing menu is "owned" by this->btParent, it is fine for us to delete it here. The Qt - // ownership in this context merely guarantees that this->btParent will, in its own destructor, delete the menu if - // it still exists. - delete this->contextMenu; - this->contextMenu = nullptr; - } - - std::optional forcedSystemOfMeasurement = - Measurement::getForcedSystemOfMeasurementForField(this->propertyName, this->configSection); - std::optional forcedRelativeScale = - Measurement::getForcedRelativeScaleForField(this->propertyName, this->configSection); - qDebug() << - Q_FUNC_INFO << "forcedSystemOfMeasurement=" << forcedSystemOfMeasurement << ", forcedRelativeScale=" << - forcedRelativeScale; - - auto fieldType = this->getBuddy().getFieldType(); - if (std::holds_alternative(fieldType)) { - return; - } - - // Since fieldType is not this->getBuddy(), we know our buddy is BtAmountEdit, not just BtLineEdit, so this cast - // should be safe. - BtAmountEdit & amountBuddy = static_cast(this->getBuddy()); - Measurement::PhysicalQuantity physicalQuantity = amountBuddy.getPhysicalQuantity(); - this->contextMenu = UnitAndScalePopUpMenu::create(this->btParent, - physicalQuantity, - forcedSystemOfMeasurement, - forcedRelativeScale); - return; -} - -void BtLabel::popContextMenu(const QPoint& point) { - // For the moment, at least, we do not allow people to choose date formats per-field. (Although you might want to - // mix and match metric and imperial systems in certain circumstances, it's less clear that there's a benefit to - // mixing and matching date formats.) - if (!std::holds_alternative(this->fieldType)) { - return; - } - - QObject* calledBy = sender(); - if (calledBy == nullptr) { - return; - } - - QWidget * widgie = qobject_cast(calledBy); - if (widgie == nullptr) { - return; - } - - this->initializeProperty(); - this->initializeSection(); - this->initializeMenu(); - - // Show the pop-up menu and get back whatever the user seleted - QAction * invoked = this->contextMenu->exec(widgie->mapToGlobal(point)); - if (invoked == nullptr) { - return; - } - - // Save the current settings (which may come from system-wide defaults) for the signal below - Q_ASSERT(std::holds_alternative(this->fieldType)); - Measurement::PhysicalQuantity physicalQuantity = std::get(this->fieldType); - PreviousScaleInfo previousScaleInfo{ - Measurement::getSystemOfMeasurementForField(this->propertyName, this->configSection, physicalQuantity), - Measurement::getForcedRelativeScaleForField(this->propertyName, this->configSection) - }; - - // To make this all work, we need to set ogMin and ogMax when og is set etc - QVector fieldsToSet; - fieldsToSet.append(this->propertyName); - if (this->propertyName == "og") { - fieldsToSet.append(QString(*PropertyNames::Style::ogMin)); - fieldsToSet.append(QString(*PropertyNames::Style::ogMax)); - } else if (this->propertyName == "fg") { - fieldsToSet.append(QString(*PropertyNames::Style::fgMin)); - fieldsToSet.append(QString(*PropertyNames::Style::fgMax)); - } else if (this->propertyName == "color_srm") { - fieldsToSet.append(QString(*PropertyNames::Style::colorMin_srm)); - fieldsToSet.append(QString(*PropertyNames::Style::colorMax_srm)); - } - - // User will either have selected a SystemOfMeasurement or a UnitSystem::RelativeScale. We can know which based on - // whether it's the menu or the sub-menu that it came from. - bool isTopMenu{invoked->parentWidget() == this->contextMenu}; - if (isTopMenu) { - // It's the menu, so SystemOfMeasurement - std::optional whatSelected = - UnitAndScalePopUpMenu::dataFromQAction(*invoked); - qDebug() << Q_FUNC_INFO << "Selected SystemOfMeasurement" << whatSelected; - if (!whatSelected) { - // Null means "Default", which means don't set a forced SystemOfMeasurement for this field - for (auto field : fieldsToSet) { - Measurement::setForcedSystemOfMeasurementForField(field, this->configSection, std::nullopt); - } - } else { - for (auto field : fieldsToSet) { - Measurement::setForcedSystemOfMeasurementForField(field, this->configSection, *whatSelected); - } - } - // Choosing a forced SystemOfMeasurement resets any selection of forced RelativeScale - for (auto field : fieldsToSet) { - Measurement::setForcedRelativeScaleForField(field, this->configSection, std::nullopt); - } - - // - // Hmm. For the color fields, we want to include the ecb or srm in the label text here. - // - // Assert that we already bailed above for fields that aren't a PhysicalQuantity, so we know std::get won't throw - // here. - // - Q_ASSERT(std::holds_alternative(this->fieldType)); - if (Measurement::PhysicalQuantity::Color == std::get(this->fieldType)) { - Measurement::UnitSystem const & disp = - Measurement::getUnitSystemForField(this->propertyName, - this->configSection, - Measurement::PhysicalQuantity::Color); - this->setText(tr("Color (%1)").arg(disp.unit()->name)); - } - } else { - // It's the sub-menu, so UnitSystem::RelativeScale - std::optional whatSelected = - UnitAndScalePopUpMenu::dataFromQAction(*invoked); - qDebug() << Q_FUNC_INFO << "Selected RelativeScale" << whatSelected; - if (!whatSelected) { - // Null means "Default", which means don't set a forced RelativeScale for this field - for (auto field : fieldsToSet) { - Measurement::setForcedRelativeScaleForField(field, this->configSection, std::nullopt); - } - } else { - for (auto field : fieldsToSet) { - Measurement::setForcedRelativeScaleForField(field, this->configSection, *whatSelected); - } - } - } - - // Remember, we need the original unit, not the new one. - emit changedSystemOfMeasurementOrScale(previousScaleInfo); - - return; -} - -BtColorLabel ::BtColorLabel (QWidget *parent) : BtLabel(parent, Measurement::PhysicalQuantity::Color ) { return; } -BtDateLabel ::BtDateLabel (QWidget *parent) : BtLabel(parent, NonPhysicalQuantity::Date ) { return; } -BtDensityLabel ::BtDensityLabel (QWidget *parent) : BtLabel(parent, Measurement::PhysicalQuantity::Density ) { return; } -BtMassLabel ::BtMassLabel (QWidget *parent) : BtLabel(parent, Measurement::PhysicalQuantity::Mass ) { return; } -BtMixedMassOrVolumeLabel::BtMixedMassOrVolumeLabel(QWidget *parent) : BtLabel(parent, Measurement::PqEitherMassOrVolume ) { return; } -BtTemperatureLabel ::BtTemperatureLabel (QWidget *parent) : BtLabel(parent, Measurement::PhysicalQuantity::Temperature ) { return; } -BtTimeLabel ::BtTimeLabel (QWidget *parent) : BtLabel(parent, Measurement::PhysicalQuantity::Time ) { return; } -BtVolumeLabel ::BtVolumeLabel (QWidget *parent) : BtLabel(parent, Measurement::PhysicalQuantity::Volume ) { return; } -BtDiastaticPowerLabel ::BtDiastaticPowerLabel (QWidget *parent) : BtLabel(parent, Measurement::PhysicalQuantity::DiastaticPower) { return; } diff --git a/src/BtLabel.h b/src/BtLabel.h deleted file mode 100644 index 1799f4fb..00000000 --- a/src/BtLabel.h +++ /dev/null @@ -1,167 +0,0 @@ -/*====================================================================================================================== - * BtLabel.h is part of Brewken, and is copyright the following authors 2009-2023: - * • Mark de Wever - * • Matt Young - * • Mik Firestone - * • Philip Greggory Lee - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#ifndef BTLABEL_H -#define BTLABEL_H -#pragma once - -#include - -#include -#include -#include -#include -#include - -#include "BtFieldType.h" -#include "BtLineEdit.h" -#include "measurement/UnitSystem.h" -#include "UiAmountWithUnits.h" // For PreviousScaleInfo - -/*! - * \class BtLabel - * - * \brief Performs the necessary magic to select display units for any label. Specifically, this allows the user to - * right-click on the label for a field and select - * (a) which unit system to use for that field (eg US Customary (mass), Imperial (mass) or Metric/SI (mass) - * for a weight field) - * (b) which units within that system to use for the field (eg kg, g, mg if the user has selected Metric/SI on - * a weight field). - * Moreover, the settings for each label are remembered (via PersistentSettings) for future times the program is - * run. - * - * This has been a rather hidden feature of the program as there were no visual clues that right-clicking on a - * field label would bring up a useful menu (and it is not common behaviour in other software). Where possible, - * we have now made it so that - * • mouseover on the label underlines the label text (hopefully making the user think of a clickable link), - * • where left-clicking would otherwise have no effect, it now has the same effect as right-click. - * - * A \c BtLabel (or subclass thereof) will usually have a corresponding \c BtLineEdit (or subclass thereof). - * These two widgets will be Qt buddies, which just means that the \c BtLineEdit accepts the input focus on - * behalf of the \c BtLabel when the user types the label's shortcut key combination. - * - * When the \c BtLabel needs to tell the \c BtLineEdit that the \c UnitSystem etc has changed, it sends a - * \c changedUnitSystemOrScale signal. (Previously this signal was called \c labelChanged.) - */ -class BtLabel : public QLabel { - Q_OBJECT - -public: - /*! - * \brief Initialize the BtLabel with the parent and do some things with the type - * - * \param parent - QWidget* to the parent object - * \param lType - the type of label: none, gravity, mass or volume - * - * \todo Not sure if I can get the name of the widget being created. - * Not sure how to signal the parent to redisplay - */ - BtLabel(QWidget * parent, BtFieldType fieldType); - virtual ~BtLabel(); - - /** - * \brief Our "buddy" should always be a \c BtLabel. This is a convenience function to get it without the caller - * having to downcast from \c QWidget etc. - */ - BtLineEdit & getBuddy() const; - - /** - * \brief We override the \c QWidget event handlers \c enterEvent and \c leaveEvent to implement mouse-over effects - * on the label text - specifically to give the user a visual clue that the label text is (right)-clickable - */ - virtual void enterEvent(QEvent* event); - virtual void leaveEvent(QEvent* event); - - /** - * \brief We override the \c QWidget event handler \c mouseReleaseEvent to capture left mouse clicks on us. (Right - * clicks get notified to us via the \c QWidget::customContextMenuRequested signal.) - */ - virtual void mouseReleaseEvent(QMouseEvent * event); - - -private: - void textEffect(bool enabled); - -public slots: - /** - * \brief Shows the pop-up menu to allow the user to override the units and/or scale for this field - */ - void popContextMenu(const QPoint &point); - -signals: - /** - * \brief Signal to say we changed the forced system of measurement and/or scale for a field (or group of fields) - * - * NB: This is mostly referenced in .ui files, which compile to string-based connection syntax (see - * https://doc.qt.io/qt-5/signalsandslots-syntaxes.html for difference from functor-based syntax that we - * generally prefer to use in .cpp files). Note too that, if you are manually editing a .ui file rather than - * using Qt Designer, you must NOT put parameter names in the function declarations in the \ and - * \ tags inside the \ tag. - * - * The idea is that fields affected by a change in forced system of measurement or scale (including to/from - * "default") can take current value, convert it to Metric/SI under the "old" settings, then redisplay it with - * whatever the new settings are. Because the fields don't store the "old" settings, we have to send them. - * (They can get the new ones just by calling \c Measurement::getUnitSystemForField() etc. - * - * There will always be an old \c SystemOfMeasurement, even if it's the global default for this field's - * \c PhysicalQuantity. There might not be an old \c RelativeScale though, hence the \c std::optional. - * - * .:TODO:. Fix this comment and/or the code - * Note that we are OK to use std::optional here as, per https://doc.qt.io/qt-5/signalsandslots.html, "Signals - * and slots can take any number of arguments of any type. They are completely type safe." HOWEVER, when - * referring to the function signature in .ui files, we need to remember to escape '<' to "<" and '>' to - * ">" because .ui files are XML. - */ - void changedSystemOfMeasurementOrScale(PreviousScaleInfo previousScaleInfo); - -// Using protected instead of private allows me to not use the friends -// declaration -protected: - BtFieldType fieldType; - QString propertyName; - QString configSection; - QWidget *btParent; - QMenu* contextMenu; - - void initializeSection(); - void initializeProperty(); - void initializeMenu(); - -}; - -// -// These are trivial specialisations of BtLabel that make it possible to use specific types of BtLabel in .ui files. -// It's a bit of a sledgehammer way to pass in a constructor parameter but seems necessary because of limitations in Qt. -// -// AFAIK there is no way to pass constructor parameters to an object in a .ui file. (If you want to do that, the advice -// seems to be to build the layout manually in C++ code.) -// -// Similarly, we might think to template BtLabel, but the Qt Meta-Object Compiler (moc) doesn't understand C++ -// templates, so we can't do that for classes that need to use the Q_OBJECT macro (required for classes that declare -// their own signals and slots or that use other services provided by Qt's meta-object system). -// -class BtColorLabel : public BtLabel { Q_OBJECT public: BtColorLabel (QWidget* parent = nullptr); }; -class BtDensityLabel : public BtLabel { Q_OBJECT public: BtDensityLabel (QWidget* parent = nullptr); }; -class BtMassLabel : public BtLabel { Q_OBJECT public: BtMassLabel (QWidget* parent = nullptr); }; -class BtTemperatureLabel : public BtLabel { Q_OBJECT public: BtTemperatureLabel (QWidget* parent = nullptr); }; -class BtVolumeLabel : public BtLabel { Q_OBJECT public: BtVolumeLabel (QWidget* parent = nullptr); }; -class BtTimeLabel : public BtLabel { Q_OBJECT public: BtTimeLabel (QWidget* parent = nullptr); }; -class BtMixedMassOrVolumeLabel : public BtLabel { Q_OBJECT public: BtMixedMassOrVolumeLabel(QWidget* parent = nullptr); }; -class BtDateLabel : public BtLabel { Q_OBJECT public: BtDateLabel (QWidget* parent = nullptr); }; -class BtDiastaticPowerLabel : public BtLabel { Q_OBJECT public: BtDiastaticPowerLabel (QWidget* parent = nullptr); }; -#endif diff --git a/src/BtLineEdit.cpp b/src/BtLineEdit.cpp deleted file mode 100644 index cd1d0ccc..00000000 --- a/src/BtLineEdit.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/*====================================================================================================================== - * BtLineEdit.cpp is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Mark de Wever - * • Mattias Måhl - * • Matt Young - * • Mike Evans - * • Mik Firestone - * • Philip Greggory Lee - * • Théophane Martin - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "BtLineEdit.h" - -#include -#include -#include - -#include "Algorithms.h" -#include "Localization.h" -#include "Logging.h" -#include "measurement/Measurement.h" -#include "model/NamedEntity.h" -#include "PersistentSettings.h" -#include "utils/OptionalHelpers.h" - -namespace { - int const min_text_size = 8; - int const max_text_size = 50; -} - -BtLineEdit::BtLineEdit(QWidget *parent, - BtFieldType fieldType, - int const defaultPrecision, - QString const & maximalDisplayString) : - QLineEdit{parent}, - fieldType{fieldType}, - defaultPrecision{defaultPrecision} { - - if (std::holds_alternative(fieldType)) { - connect(this, &QLineEdit::editingFinished, this, &BtLineEdit::onLineChanged); - } - - // We can work out (and store) our display size here, but not yet set it. The way the Designer UI Files work is to - // generate code that calls setters such as setMaximumWidth() etc, which would override anything we do here in the - // constructor. So we set our size when setText() is called. - this->calculateDisplaySize(maximalDisplayString); - - return; -} - -BtLineEdit::~BtLineEdit() = default; - -BtFieldType const BtLineEdit::getFieldType() const { - return this->fieldType; -} - -template T BtLineEdit::getValueAs() const { - qDebug() << Q_FUNC_INFO << "Converting" << this->text() << "to" << Measurement::extractRawFromString(this->text()); - return Measurement::extractRawFromString(this->text()); -} -// -// Instantiate the above template function 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.) -// -template int BtLineEdit::getValueAs() const; -template unsigned int BtLineEdit::getValueAs() const; -template double BtLineEdit::getValueAs() const; - -void BtLineEdit::onLineChanged() { - qDebug() << Q_FUNC_INFO; - if (sender() == this) { - emit textModified(); - } - return; -} - -void BtLineEdit::setText(std::optional amount, std::optional precision) { - // For percentages, we'd like to show the % symbol after the number - QString symbol{""}; - if (NonPhysicalQuantity::Percentage == std::get(this->fieldType)) { - symbol = " %"; - } - if (amount) { - this->QLineEdit::setText( - Measurement::displayQuantity(*amount, precision.value_or(this->defaultPrecision)) + symbol - ); - } else { - this->QLineEdit::setText(""); - } - this->setDisplaySize(); - return; -} - -void BtLineEdit::setText(QString amount, std::optional precision) { - if (!amount.isEmpty() && NonPhysicalQuantity::String != std::get(this->fieldType)) { - bool ok = false; - double amt = Measurement::extractRawFromString(amount, &ok); - if (!ok) { - qWarning() << Q_FUNC_INFO << "Could not convert" << amount << "to double"; - } - this->setText(amt, precision.value_or(this->defaultPrecision)); - return; - } - - this->QLineEdit::setText(amount); - this->setDisplaySize(true); - return; -} - -void BtLineEdit::calculateDisplaySize(QString const & maximalDisplayString) { - // - // By default, some, but not all, boxes have a min and max width of 100 pixels, but this is not wide enough on a - // high DPI display. We instead calculate width here based on font-size - but without reducing any existing minimum - // width. - // - // Unfortunately, for a QLineEdit object, calculating the width is hard because, besides the text, we need to allow - // for the width of padding and frame, which is non-trivial to discover. Eg, typically: - // marginsAroundText() and contentsMargins() both return 0 for left and right margins - // contentsRect() and frameSize() both give the same width as width() - // AFAICT, the best option is to query via pixelMetric() calls to the widget's style, but we need to check this works - // in practice on a variety of different systems. - // - QFontMetrics displayFontMetrics(this->font()); - QRect minimumTextRect = displayFontMetrics.boundingRect(maximalDisplayString); - QMargins marginsAroundText = this->textMargins(); - auto myStyle = this->style(); - // NB: 2× frame width as on left and right; same for horizontal spacing - int totalWidgetWidthForMaximalDisplayString = minimumTextRect.width() + - marginsAroundText.left() + - marginsAroundText.right() + - (2 * myStyle->pixelMetric(QStyle::PM_DefaultFrameWidth)) + - (2 * myStyle->pixelMetric(QStyle::PM_LayoutHorizontalSpacing)); - - this->desiredWidthInPixels = qMax(this->minimumWidth(), totalWidgetWidthForMaximalDisplayString); - return; -} - -void BtLineEdit::setDisplaySize(bool recalculate) { - if ( recalculate ) { - QString sizing_string = text(); - - // this is a dirty bit of cheating. If we do not reset the minimum - // width, the field only ever gets bigger. This forces the resize I - // want, but only when we are instructed to force it - setMinimumWidth(0); - if ( sizing_string.length() < min_text_size ) { - sizing_string = QString(min_text_size,'a'); - } else if ( sizing_string.length() > max_text_size ) { - sizing_string = QString(max_text_size,'a'); - } - calculateDisplaySize(sizing_string); - } - this->setFixedWidth(this->desiredWidthInPixels); - return; -} - - -BtGenericEdit ::BtGenericEdit (QWidget *parent) : BtLineEdit(parent, NonPhysicalQuantity::String ) { return; } -BtStringEdit ::BtStringEdit (QWidget *parent) : BtLineEdit(parent, NonPhysicalQuantity::String ) { return; } -BtPercentageEdit ::BtPercentageEdit (QWidget *parent) : BtLineEdit(parent, NonPhysicalQuantity::Percentage , 0) { return; } -BtDimensionlessEdit::BtDimensionlessEdit(QWidget *parent) : BtLineEdit(parent, NonPhysicalQuantity::Dimensionless, 3) { return; } diff --git a/src/BtLineEdit.h b/src/BtLineEdit.h deleted file mode 100644 index eaa5ad84..00000000 --- a/src/BtLineEdit.h +++ /dev/null @@ -1,139 +0,0 @@ -/*====================================================================================================================== - * BtLineEdit.h is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Mark de Wever - * • Matt Young - * • Mike Evans - * • Mik Firestone - * • Philip Greggory Lee - * • Scott Peshak - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#ifndef BTLINEEDIT_H -#define BTLINEEDIT_H -#pragma once - -#include - -#include -#include -#include - -#include "BtFieldType.h" - -class NamedEntity; - -/*! - * \class BtLineEdit - * - * \brief This class and its subclasses extends QLineEdit such that the Object handles all the unit transformation we - * do, instead of each dialog. - * - * It makes the code much nicer and prevents more cut'n'paste code. - * - * A \c BtLineEdit (or subclass thereof) will usually have a corresponding \c BtLabel (or subclass thereof). - * See comment in BtLabel.h for more details on the relationship between the two classes. - */ -class BtLineEdit : public QLineEdit { - Q_OBJECT - -public: - /*! - * \brief Initialize the BtLineEdit with the parent and do some things with the type - * - * \param parent - QWidget* to the parent object - * \param fieldType the type of input field; if it is not \c NonPhysicalQuantity then we should be being called - * from \c BtAmountEdit or a subclass thereof - * \param defaultPrecision - * \param maximalDisplayString - an example of the widest string this widget would be expected to need to display - * - * \todo Not sure if I can get the name of the widget being created. - * Not sure how to signal the parent to redisplay - */ - BtLineEdit(QWidget* parent = nullptr, - BtFieldType fieldType = NonPhysicalQuantity::String, - int const defaultPrecision = 3, - QString const & maximalDisplayString = "100.000 srm"); - - virtual ~BtLineEdit(); - - BtFieldType const getFieldType() const; - - /** - * \brief Set the amount for a decimal field - * - * \param amount is the amount to display, but the field should be blank if this is \b std::nullopt - * \param precision is how many decimal places to show. If not specified, the default will be used. - */ - void setText(std::optional amount, std::optional precision = std::nullopt); - - /** - * \brief .:TBD:. Do we need this to be able to parse numbers out of strings, or just to set string text? - */ - void setText(QString amount, std::optional precision = std::nullopt); - - /** - * \brief Use this when you want to get the text as a number (and ignore any units or other trailling letters or - * symbols) - */ - template T getValueAs() const; - -public slots: - /** - * \brief This slot receives the \c QLineEdit::editingFinished signal - */ - void onLineChanged(); - -signals: - /** - * \brief Where we want "instant updates", this signal should be picked up by the editor or widget object using this - * input field so it can read the changed value and update the underlying data model. - * - * Where we want to defer updating the underlying data model until the user clicks "Save" etc, then this - * signal will typically be ignored. - */ - void textModified(); - -protected: - BtFieldType fieldType; - - int const defaultPrecision; - - void calculateDisplaySize(QString const & maximalDisplayString); - void setDisplaySize(bool recalculate = false); - - int desiredWidthInPixels; -}; - -// -// These are trivial specialisations of BtLineEdit that make it possible to use specific types of BtLineEdit in .ui -// files. It's a bit of a sledgehammer way to pass in a constructor parameter but seems necessary because of -// limitations in Qt. -// -// AFAIK there is no way to pass constructor parameters to an object in a .ui file. (If you want to do that, the advice -// seems to be to build the layout manually in C++ code.) -// -// Similarly, we might think to template BtLineEdit, but the Qt Meta-Object Compiler (moc) doesn't understand C++ -// templates. This means we can't template classes that need to use the Q_OBJECT macro (required for classes that -// declare their own signals and slots or that use other services provided by Qt's meta-object system). -// -// TODO: Kill BtGenericEdit -// -// TBD: Can we think of a more elegant way of handing, eg, different numbers of decimal places for % -// -class BtGenericEdit : public BtLineEdit { Q_OBJECT public: BtGenericEdit (QWidget* parent); }; -class BtStringEdit : public BtLineEdit { Q_OBJECT public: BtStringEdit (QWidget* parent); }; -class BtPercentageEdit : public BtLineEdit { Q_OBJECT public: BtPercentageEdit (QWidget* parent); }; -class BtDimensionlessEdit : public BtLineEdit { Q_OBJECT public: BtDimensionlessEdit(QWidget* parent); }; - -#endif diff --git a/src/BtTreeItem.cpp b/src/BtTreeItem.cpp index 252f31fc..ae828ee2 100644 --- a/src/BtTreeItem.cpp +++ b/src/BtTreeItem.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * BtTreeItem.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * BtTreeItem.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Daniel Pettersson * • Greg Meess * • Mattias Måhl @@ -283,10 +283,8 @@ QVariant BtTreeItem::dataFermentable(int column) { break; case FERMENTABLECOLORCOL: if (ferm) { - return QVariant(Measurement::displayAmount(Measurement::Amount{ferm->color_srm(), Measurement::Units::srm}, - BtString::EMPTY_STR, - PropertyNames::Fermentable::color_srm, - 0)); + return QVariant(Measurement::displayAmount(Measurement::Amount{ferm->color_srm(), + Measurement::Units::srm}, 0)); } break; default : diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5bf12476..f0f875ae 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,13 +48,10 @@ set(filesToCompile_cpp ${repoDir}/src/BrewDayFormatter.cpp ${repoDir}/src/BrewDayScrollWidget.cpp ${repoDir}/src/BrewNoteWidget.cpp - ${repoDir}/src/BtAmountEdit.cpp ${repoDir}/src/BtColor.cpp ${repoDir}/src/BtDatePopup.cpp ${repoDir}/src/BtFieldType.cpp ${repoDir}/src/BtFolder.cpp - ${repoDir}/src/BtLabel.cpp - ${repoDir}/src/BtLineEdit.cpp ${repoDir}/src/BtSplashScreen.cpp ${repoDir}/src/BtTabWidget.cpp ${repoDir}/src/BtTextEdit.cpp @@ -153,6 +150,8 @@ set(filesToCompile_cpp ${repoDir}/src/RefractoDialog.cpp ${repoDir}/src/ScaleRecipeTool.cpp ${repoDir}/src/SimpleUndoableUpdate.cpp + ${repoDir}/src/SmartAmounts.cpp + ${repoDir}/src/SmartField.cpp ${repoDir}/src/StrikeWaterDialog.cpp ${repoDir}/src/StyleButton.cpp ${repoDir}/src/StyleEditor.cpp @@ -171,7 +170,6 @@ set(filesToCompile_cpp ${repoDir}/src/TimerListDialog.cpp ${repoDir}/src/TimerMainDialog.cpp ${repoDir}/src/TimerWidget.cpp - ${repoDir}/src/UiAmountWithUnits.cpp ${repoDir}/src/utils/BtException.cpp ${repoDir}/src/utils/BtStringConst.cpp ${repoDir}/src/utils/BtStringStream.cpp @@ -187,9 +185,8 @@ set(filesToCompile_cpp ${repoDir}/src/WaterSortFilterProxyModel.cpp ${repoDir}/src/WaterTableWidget.cpp ${repoDir}/src/widgets/Animator.cpp - ${repoDir}/src/widgets/BtAmountDigitWidget.cpp - ${repoDir}/src/widgets/BtDigitWidget.cpp ${repoDir}/src/widgets/SelectionControl.cpp + ${repoDir}/src/widgets/SmartDigitWidget.cpp ${repoDir}/src/widgets/SmartLabel.cpp ${repoDir}/src/widgets/SmartLineEdit.cpp ${repoDir}/src/widgets/ToggleSwitch.cpp diff --git a/src/EquipmentEditor.cpp b/src/EquipmentEditor.cpp index 323cce1d..2e8c0a37 100644 --- a/src/EquipmentEditor.cpp +++ b/src/EquipmentEditor.cpp @@ -31,8 +31,6 @@ #include #include "BtHorizontalTabs.h" -#include "BtLabel.h" -#include "BtLineEdit.h" #include "database/ObjectStoreWrapper.h" #include "EquipmentListModel.h" #include "HeatCalculations.h" @@ -72,38 +70,21 @@ EquipmentEditor::EquipmentEditor(QWidget* parent, bool singleEquipEditor) : obsEquip = nullptr; - SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_tunSpecificHeat, PropertyNames::Equipment::tunSpecificHeat_calGC, *this->label_tunSpecificHeat ); - SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_grainAbsorption, PropertyNames::Equipment::grainAbsorption_LKg ); - SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_hopUtilization , PropertyNames::Equipment::hopUtilization_pct , 0); - SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_tunWeight , PropertyNames::Equipment::tunWeight_kg , *this->label_tunWeight ); - SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_name , PropertyNames::NamedEntity::name ); - SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_boilingPoint , PropertyNames::Equipment::boilingPoint_c , *this->label_boilingPoint , 1); - SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_boilTime , PropertyNames::Equipment::boilTime_min , *this->label_boilTime ); - SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_batchSize , PropertyNames::Equipment::batchSize_l , *this->label_batchSize ); - SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_boilSize , PropertyNames::Equipment::boilSize_l , *this->label_boilSize ); - SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_evaporationRate, PropertyNames::Equipment::evapRate_lHr , *this->label_evaporationRate ); - SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_lauterDeadspace, PropertyNames::Equipment::lauterDeadspace_l , *this->label_lauterDeadspace ); - SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_topUpKettle , PropertyNames::Equipment::topUpKettle_l , *this->label_topUpKettle ); - SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_topUpWater , PropertyNames::Equipment::topUpWater_l , *this->label_topUpWater ); - SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_trubChillerLoss, PropertyNames::Equipment::trubChillerLoss_l , *this->label_trubChillerLoss ); - SMART_LINE_EDIT_INIT(EquipmentEditor, Equipment, lineEdit_tunVolume , PropertyNames::Equipment::tunVolume_l , *this->label_tunVolume ); - - -/// this->lineEdit_tunSpecificHeat->init(Equipment::typeLookup.getType(PropertyNames::Equipment::tunSpecificHeat_calGC), *this->label_tunSpecificHeat ); -/// this->lineEdit_grainAbsorption->init(Equipment::typeLookup.getType(PropertyNames::Equipment::grainAbsorption_LKg ) ); -/// this->lineEdit_hopUtilization ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::hopUtilization_pct ) , 0); // label_hopUtilization -/// this->lineEdit_tunWeight ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::tunWeight_kg ), *this->label_tunWeight ); -/// this->lineEdit_name ->init(Equipment::typeLookup.getType(PropertyNames::NamedEntity::name ) ); -/// this->lineEdit_boilingPoint ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::boilingPoint_c ), *this->label_boilingPoint , 1); -/// this->lineEdit_boilTime ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::boilTime_min ), *this->label_boilTime ); -/// this->lineEdit_batchSize ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::batchSize_l ), *this->label_batchSize ); -/// this->lineEdit_boilSize ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::boilSize_l ), *this->label_boilSize ); -/// this->lineEdit_evaporationRate->init(Equipment::typeLookup.getType(PropertyNames::Equipment::evapRate_lHr ), *this->label_evaporationRate ); -/// this->lineEdit_lauterDeadspace->init(Equipment::typeLookup.getType(PropertyNames::Equipment::lauterDeadspace_l ), *this->label_lauterDeadspace ); -/// this->lineEdit_topUpKettle ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::topUpKettle_l ), *this->label_topUpKettle ); -/// this->lineEdit_topUpWater ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::topUpWater_l ), *this->label_topUpWater ); -/// this->lineEdit_trubChillerLoss->init(Equipment::typeLookup.getType(PropertyNames::Equipment::trubChillerLoss_l ), *this->label_trubChillerLoss ); -/// this->lineEdit_tunVolume ->init(Equipment::typeLookup.getType(PropertyNames::Equipment::tunVolume_l ), *this->label_tunVolume ); + SMART_FIELD_INIT(EquipmentEditor, label_tunSpecificHeat, lineEdit_tunSpecificHeat, Equipment, PropertyNames::Equipment::tunSpecificHeat_calGC ); + SMART_FIELD_INIT(EquipmentEditor, label_grainAbsorption, lineEdit_grainAbsorption, Equipment, PropertyNames::Equipment::grainAbsorption_LKg ); + SMART_FIELD_INIT(EquipmentEditor, label_hopUtilization , lineEdit_hopUtilization , Equipment, PropertyNames::Equipment::hopUtilization_pct , 0); + SMART_FIELD_INIT(EquipmentEditor, label_tunWeight , lineEdit_tunWeight , Equipment, PropertyNames::Equipment::tunWeight_kg ); + SMART_FIELD_INIT(EquipmentEditor, label_name , lineEdit_name , Equipment, PropertyNames::NamedEntity::name ); + 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_batchSize , lineEdit_batchSize , Equipment, PropertyNames::Equipment::batchSize_l ); + SMART_FIELD_INIT(EquipmentEditor, label_boilSize , lineEdit_boilSize , Equipment, PropertyNames::Equipment::boilSize_l ); + SMART_FIELD_INIT(EquipmentEditor, label_evaporationRate, lineEdit_evaporationRate, Equipment, PropertyNames::Equipment::evapRate_lHr ); + SMART_FIELD_INIT(EquipmentEditor, label_lauterDeadspace, lineEdit_lauterDeadspace, Equipment, PropertyNames::Equipment::lauterDeadspace_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_trubChillerLoss, lineEdit_trubChillerLoss, Equipment, PropertyNames::Equipment::trubChillerLoss_l ); + SMART_FIELD_INIT(EquipmentEditor, label_tunVolume , lineEdit_tunVolume , Equipment, PropertyNames::Equipment::tunVolume_l ); // Connect all the edit boxen connect(lineEdit_boilTime, &SmartLineEdit::textModified, this, &EquipmentEditor::updateCheckboxRecord ); diff --git a/src/FermentableDialog.cpp b/src/FermentableDialog.cpp index e703e28b..8ba63d75 100644 --- a/src/FermentableDialog.cpp +++ b/src/FermentableDialog.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * FermentableDialog.cpp is part of Brewken, and is copyright the following authors 2009-2021: + * FermentableDialog.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Daniel Pettersson * • Matt Young @@ -39,8 +39,7 @@ FermentableDialog::FermentableDialog(MainWindow* parent) : QDialog(parent), mainWindow(parent), fermEdit(new FermentableEditor(this)), - numFerms(0) -{ + numFerms(0) { doLayout(); fermTableModel = new FermentableTableModel(tableWidget, false); @@ -49,22 +48,24 @@ FermentableDialog::FermentableDialog(MainWindow* parent) : fermTableProxy->setSourceModel(fermTableModel); tableWidget->setModel(fermTableProxy); tableWidget->setSortingEnabled(true); - tableWidget->sortByColumn( FERMNAMECOL, Qt::AscendingOrder ); + tableWidget->sortByColumn(static_cast(FermentableTableModel::ColumnIndex::Name), Qt::AscendingOrder ); fermTableProxy->setDynamicSortFilter(true); fermTableProxy->setFilterKeyColumn(1); - connect( pushButton_addToRecipe, SIGNAL( clicked() ), this, SLOT( addFermentable() ) ); - connect( pushButton_edit, &QAbstractButton::clicked, this, &FermentableDialog::editSelected ); - connect( pushButton_remove, &QAbstractButton::clicked, this, &FermentableDialog::removeFermentable ); - connect( pushButton_new, SIGNAL( clicked() ), this, SLOT( newFermentable() ) ); - connect( tableWidget, &QAbstractItemView::doubleClicked, this, &FermentableDialog::addFermentable ); - connect( qLineEdit_searchBox, &QLineEdit::textEdited, this, &FermentableDialog::filterFermentables); +// connect(this->pushButton_addToRecipe, &QAbstractButton::clicked, this, &FermentableDialog::addFermentable ); .:TODO:. Work out what this is supposed to do! + connect(this->pushButton_edit, &QAbstractButton::clicked, this, &FermentableDialog::editSelected ); + connect(this->pushButton_remove, &QAbstractButton::clicked, this, &FermentableDialog::removeFermentable); + // Note, per https://wiki.qt.io/New_Signal_Slot_Syntax#Default_arguments_in_slot, the use of a trivial lambda + // function to allow use of default argument on newFermentable() slot + connect(this->pushButton_new , &QAbstractButton::clicked, this, [this]() { this->newFermentable(); return; } ); + connect(this->tableWidget, &QAbstractItemView::doubleClicked, this, &FermentableDialog::addFermentable ); + connect(this->qLineEdit_searchBox, &QLineEdit::textEdited, this, &FermentableDialog::filterFermentables); // Let me see if this works fermTableModel->observeDatabase(true); + return; } -void FermentableDialog::doLayout() -{ +void FermentableDialog::doLayout() { resize(800, 300); verticalLayout = new QVBoxLayout(this); tableWidget = new QTableView(this); @@ -186,16 +187,15 @@ void FermentableDialog::addFermentable(const QModelIndex& index) { } translated = fermTableProxy->mapToSource(selected[0]); - } - else - { + } else { // Only respond if the name is selected. Since we connect to double-click signal, // this keeps us from adding something to the recipe when we just want to edit // one of the other fermentable fields. - if( index.column() == FERMNAMECOL ) + if (index.column() == static_cast(FermentableTableModel::ColumnIndex::Name)) { translated = fermTableProxy->mapToSource(index); - else + } else { return; + } } MainWindow::instance().addFermentableToRecipe(fermTableModel->getRow(translated.row())); @@ -203,27 +203,25 @@ void FermentableDialog::addFermentable(const QModelIndex& index) { return; } -void FermentableDialog::newFermentable(QString folder) -{ +void FermentableDialog::newFermentable(QString folder) { QString name = QInputDialog::getText(this, tr("Fermentable name"), tr("Fermentable name:")); - if( name.isEmpty() ) + if (name.isEmpty()) { return; + } Fermentable* ferm = new Fermentable(name); - if ( ! folder.isEmpty() ) + if (!folder.isEmpty()) { ferm->setFolder(folder); + } fermEdit->setFermentable(ferm); fermEdit->show(); + return; } -void FermentableDialog::newFermentable() { - newFermentable(QString()); -} - -void FermentableDialog::filterFermentables(QString searchExpression) -{ - fermTableProxy->setFilterCaseSensitivity(Qt::CaseInsensitive); - fermTableProxy->setFilterFixedString(searchExpression); +void FermentableDialog::filterFermentables(QString searchExpression) { + fermTableProxy->setFilterCaseSensitivity(Qt::CaseInsensitive); + fermTableProxy->setFilterFixedString(searchExpression); + return; } diff --git a/src/FermentableDialog.h b/src/FermentableDialog.h index a44632c2..88d09733 100644 --- a/src/FermentableDialog.h +++ b/src/FermentableDialog.h @@ -60,7 +60,6 @@ class FermentableDialog : public QDialog { QPushButton *pushButton_remove; //! @} - void newFermentable(QString folder); public slots: /*! If \b index is the default, will add the selected fermentable to list. * Otherwise, will add the fermentable at the specified index. @@ -71,7 +70,7 @@ public slots: void filterFermentables(QString searchExpression); //void changed(QMetaProperty,QVariant); - void newFermentable(); + void newFermentable(QString folder = ""); protected: diff --git a/src/FermentableEditor.cpp b/src/FermentableEditor.cpp index 72a85fb9..14cc033c 100644 --- a/src/FermentableEditor.cpp +++ b/src/FermentableEditor.cpp @@ -42,18 +42,18 @@ FermentableEditor::FermentableEditor(QWidget* parent) : Fermentable::typeStringMapping.enumToString(ii)); } - SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_name , PropertyNames::NamedEntity::name ); - SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_color , PropertyNames::Fermentable::color_srm , *this->label_color , 0); - SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_diastaticPower, PropertyNames::Fermentable::diastaticPower_lintner, *this->label_diastaticPower ); - SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_coarseFineDiff, PropertyNames::Fermentable::coarseFineDiff_pct , 0); - SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_ibuGalPerLb , PropertyNames::Fermentable::ibuGalPerLb , 0); - SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_maxInBatch , PropertyNames::Fermentable::maxInBatch_pct , 0); - SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_moisture , PropertyNames::Fermentable::moisture_pct , 0); - SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_protein , PropertyNames::Fermentable::protein_pct , 0); - SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_yield , PropertyNames::Fermentable::yield_pct , 1); - SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_inventory , PropertyNames::Fermentable::amount , *this->label_inventory ); - SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_origin , PropertyNames::Fermentable::origin ); - SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_supplier , PropertyNames::Fermentable::supplier ); + 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_yield , lineEdit_yield , Fermentable, PropertyNames::Fermentable::yield_pct , 1); + SMART_FIELD_INIT(FermentableEditor, label_inventory , lineEdit_inventory , Fermentable, PropertyNames::Fermentable::amount ); + SMART_FIELD_INIT(FermentableEditor, label_origin , lineEdit_origin , Fermentable, PropertyNames::Fermentable::origin ); + SMART_FIELD_INIT(FermentableEditor, label_supplier , lineEdit_supplier , Fermentable, PropertyNames::Fermentable::supplier ); connect(pushButton_new, &QAbstractButton::clicked, this, &FermentableEditor::clickedNewFermentable); connect(pushButton_save, &QAbstractButton::clicked, this, &FermentableEditor::save); diff --git a/src/FermentableSortFilterProxyModel.cpp b/src/FermentableSortFilterProxyModel.cpp index 1518c14d..91b141a8 100644 --- a/src/FermentableSortFilterProxyModel.cpp +++ b/src/FermentableSortFilterProxyModel.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * FermentableSortFilterProxyModel.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * FermentableSortFilterProxyModel.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Daniel Pettersson * • Jamie Daws * • Matt Young @@ -42,8 +42,9 @@ bool FermentableSortFilterProxyModel::lessThan(QModelIndex const & left, QVariant leftFermentable = sourceModel()->data(left); QVariant rightFermentable = sourceModel()->data(right); - switch (left.column()) { - case FERMINVENTORYCOL: + auto const columnIndex = static_cast(left.column()); + switch (columnIndex) { + case FermentableTableModel::ColumnIndex::Inventory: // If the numbers are equal, compare the names and be done with it if (Measurement::qStringToSI(leftFermentable.toString(), Measurement::PhysicalQuantity::Mass) == Measurement::qStringToSI(rightFermentable.toString(), Measurement::PhysicalQuantity::Mass)) { @@ -57,7 +58,7 @@ bool FermentableSortFilterProxyModel::lessThan(QModelIndex const & left, return Measurement::qStringToSI(leftFermentable.toString(), Measurement::PhysicalQuantity::Mass) < Measurement::qStringToSI(rightFermentable.toString(), Measurement::PhysicalQuantity::Mass); - case FERMAMOUNTCOL: + case FermentableTableModel::ColumnIndex::Amount: // If the numbers are equal, compare the names and be done with it if (Measurement::qStringToSI(leftFermentable.toString(), Measurement::PhysicalQuantity::Mass) == Measurement::qStringToSI(rightFermentable.toString(), Measurement::PhysicalQuantity::Mass)) { @@ -66,7 +67,7 @@ bool FermentableSortFilterProxyModel::lessThan(QModelIndex const & left, return Measurement::qStringToSI(leftFermentable.toString(), Measurement::PhysicalQuantity::Mass) < Measurement::qStringToSI(rightFermentable.toString(), Measurement::PhysicalQuantity::Mass); - case FERMYIELDCOL: + case FermentableTableModel::ColumnIndex::Yield: { double leftDouble = toDouble(leftFermentable); double rightDouble = toDouble(rightFermentable); @@ -77,7 +78,7 @@ bool FermentableSortFilterProxyModel::lessThan(QModelIndex const & left, return leftDouble < rightDouble; } - case FERMCOLORCOL: + case FermentableTableModel::ColumnIndex::Color: { auto leftAmount = Measurement::qStringToSI(leftFermentable.toString(), Measurement::PhysicalQuantity::Color); @@ -97,14 +98,15 @@ double FermentableSortFilterProxyModel::toDouble(QVariant side) const { return Localization::toDouble(side.toString(), Q_FUNC_INFO); } -QString FermentableSortFilterProxyModel::getName( const QModelIndex &index ) const -{ - QVariant info = sourceModel()->data(QAbstractItemModel::createIndex(index.row(),FERMNAMECOL)); +QString FermentableSortFilterProxyModel::getName( const QModelIndex &index ) const { + QVariant info = sourceModel()->data( + QAbstractItemModel::createIndex(index.row(), + static_cast(FermentableTableModel::ColumnIndex::Name))) + ; return info.toString(); } -bool FermentableSortFilterProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent) const -{ +bool FermentableSortFilterProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent) const { FermentableTableModel* model = qobject_cast(sourceModel()); QModelIndex index = sourceModel()->index(source_row, 0, source_parent); diff --git a/src/HopDialog.cpp b/src/HopDialog.cpp index f82c7a2d..f59b5c12 100644 --- a/src/HopDialog.cpp +++ b/src/HopDialog.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * HopDialog.cpp is part of Brewken, and is copyright the following authors 2009-2021: + * HopDialog.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Daniel Pettersson * • Luke Vincent @@ -27,7 +27,6 @@ #include #include -//#include "database/Database.h" #include "database/ObjectStoreWrapper.h" #include "HopEditor.h" #include "HopSortFilterProxyModel.h" @@ -40,8 +39,7 @@ HopDialog::HopDialog(MainWindow* parent) : QDialog(parent), mainWindow(parent), hopEditor(new HopEditor(this)), - numHops(0) -{ + numHops(0) { doLayout(); hopTableModel = new HopTableModel(tableWidget, false); @@ -50,22 +48,24 @@ HopDialog::HopDialog(MainWindow* parent) : hopTableProxy->setSourceModel(hopTableModel); tableWidget->setModel(hopTableProxy); tableWidget->setSortingEnabled(true); - tableWidget->sortByColumn( HOPNAMECOL, Qt::AscendingOrder ); + tableWidget->sortByColumn(static_cast(HopTableModel::ColumnIndex::Name), Qt::AscendingOrder ); hopTableProxy->setDynamicSortFilter(true); hopTableProxy->setFilterKeyColumn(1); - connect( pushButton_addToRecipe, SIGNAL( clicked() ), this, SLOT( addHop() ) ); - connect( pushButton_edit, &QAbstractButton::clicked, this, &HopDialog::editSelected ); - connect( pushButton_new, SIGNAL( clicked() ), this, SLOT( newHop() ) ); - connect( pushButton_remove, &QAbstractButton::clicked, this, &HopDialog::removeHop); - connect( tableWidget, &QAbstractItemView::doubleClicked, this, &HopDialog::addHop ); - connect( qLineEdit_searchBox, &QLineEdit::textEdited, this, &HopDialog::filterHops); + // Note, per https://wiki.qt.io/New_Signal_Slot_Syntax#Default_arguments_in_slot, the use of a trivial lambda + // function to allow use of default argument on newHop() slot +/// connect(this->pushButton_addToRecipe, &QAbstractButton::clicked, this, &HopDialog::addHop ); .:TODO:. Work out what this is supposed to do! + connect(this->pushButton_edit, &QAbstractButton::clicked, this, &HopDialog::editSelected); + connect(this->pushButton_new, &QAbstractButton::clicked, this, [this]() { this->newHop(); return; } ); + connect(this->pushButton_remove, &QAbstractButton::clicked, this, &HopDialog::removeHop ); + connect(this->tableWidget, &QAbstractItemView::doubleClicked, this, &HopDialog::addHop ); + connect(this->qLineEdit_searchBox, &QLineEdit::textEdited, this, &HopDialog::filterHops ); hopTableModel->observeDatabase(true); + return; } -void HopDialog::doLayout() -{ +void HopDialog::doLayout() { resize(800, 300); verticalLayout = new QVBoxLayout(this); tableWidget = new QTableView(this); @@ -145,11 +145,9 @@ void HopDialog::removeHop() { return; } -void HopDialog::addHop(const QModelIndex& index) -{ +void HopDialog::addHop(const QModelIndex& index) { QModelIndex translated; - if( !index.isValid() ) - { + if (!index.isValid()) { QModelIndexList selected = tableWidget->selectionModel()->selectedIndexes(); int row, size, i; @@ -166,16 +164,15 @@ void HopDialog::addHop(const QModelIndex& index) } translated = hopTableProxy->mapToSource(selected.value(0)); - } - else - { + } else { // Only respond if the name is selected. Since we connect to double-click signal, // this keeps us from adding something to the recipe when we just want to edit // one of the other columns. - if( index.column() == HOPNAMECOL ) + if (index.column() == static_cast(HopTableModel::ColumnIndex::Name)) { translated = hopTableProxy->mapToSource(index); - else + } else { return; + } } MainWindow::instance().addHopToRecipe(hopTableModel->getRow(translated.row())); @@ -208,25 +205,22 @@ void HopDialog::editSelected() return; } -void HopDialog::newHop() -{ - newHop(QString()); -} - -void HopDialog::newHop(QString folder) -{ +void HopDialog::newHop(QString folder) { QString name = QInputDialog::getText(this, tr("Hop name"), tr("Hop name:")); - if( name.isEmpty() ) + if( name.isEmpty() ) { return; + } // .:TODO:. Change to shared_ptr as potential memory leak Hop* hop = new Hop(name); - if ( ! folder.isEmpty() ) + if ( ! folder.isEmpty() ) { hop->setFolder(folder); + } hopEditor->setHop(hop); hopEditor->show(); + return; } void HopDialog::filterHops(QString searchExpression) diff --git a/src/HopDialog.h b/src/HopDialog.h index de20220e..2280c9ec 100644 --- a/src/HopDialog.h +++ b/src/HopDialog.h @@ -61,7 +61,6 @@ class HopDialog : public QDialog QLineEdit *qLineEdit_searchBox; //! @} - void newHop(QString folder); public slots: //! Add selected hop to current recipe. @@ -71,7 +70,7 @@ public slots: //! Bring up the editor for the selected hop. void editSelected(); //! Create a new hop. - void newHop(); + void newHop(QString folder = ""); //! FIlters the shown hops void filterHops(QString searchExpression); diff --git a/src/HopEditor.cpp b/src/HopEditor.cpp index 1e752d78..4da06202 100644 --- a/src/HopEditor.cpp +++ b/src/HopEditor.cpp @@ -37,31 +37,31 @@ HopEditor::HopEditor(QWidget * parent) : this->tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_name , PropertyNames::NamedEntity::name ); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_alpha , PropertyNames::Hop::alpha_pct , 0); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_inventory , PropertyNames::Hop::amount_kg , *this->label_inventory ); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_time , PropertyNames::Hop::time_min , *this->label_time , 0); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_beta , PropertyNames::Hop::beta_pct , 0); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_HSI , PropertyNames::Hop::hsi_pct , 0); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_origin , PropertyNames::Hop::origin ); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_humulene , PropertyNames::Hop::humulene_pct , 0); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_caryophyllene , PropertyNames::Hop::caryophyllene_pct , 0); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_cohumulone , PropertyNames::Hop::cohumulone_pct , 0); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_myrcene , PropertyNames::Hop::myrcene_pct , 0); + 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 , 0); + SMART_FIELD_INIT(HopEditor, label_inventory , lineEdit_inventory , Hop, PropertyNames::Hop::amount_kg ); + SMART_FIELD_INIT(HopEditor, label_time , lineEdit_time , Hop, PropertyNames::Hop::time_min , 0); + SMART_FIELD_INIT(HopEditor, label_beta , lineEdit_beta , Hop, PropertyNames::Hop::beta_pct , 0); + 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 , 0); + SMART_FIELD_INIT(HopEditor, label_caryophyllene , lineEdit_caryophyllene , Hop, PropertyNames::Hop::caryophyllene_pct , 0); + SMART_FIELD_INIT(HopEditor, label_cohumulone , lineEdit_cohumulone , Hop, PropertyNames::Hop::cohumulone_pct , 0); + SMART_FIELD_INIT(HopEditor, label_myrcene , lineEdit_myrcene , Hop, PropertyNames::Hop::myrcene_pct , 0); // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_producer , PropertyNames::Hop::producer ); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_product_id , PropertyNames::Hop::product_id ); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_year , PropertyNames::Hop::year ); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_total_oil_ml_per_100g, PropertyNames::Hop::total_oil_ml_per_100g ); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_farnesene , PropertyNames::Hop::farnesene_pct , 0); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_geraniol , PropertyNames::Hop::geraniol_pct , 0); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_b_pinene , PropertyNames::Hop::b_pinene_pct , 0); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_linalool , PropertyNames::Hop::linalool_pct , 0); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_limonene , PropertyNames::Hop::limonene_pct , 0); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_nerol , PropertyNames::Hop::nerol_pct , 0); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_pinene , PropertyNames::Hop::pinene_pct , 0); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_polyphenols , PropertyNames::Hop::polyphenols_pct , 0); - SMART_LINE_EDIT_INIT(HopEditor, Hop, lineEdit_xanthohumol , PropertyNames::Hop::xanthohumol_pct , 0); + SMART_FIELD_INIT(HopEditor, label_producer , lineEdit_producer , Hop, PropertyNames::Hop::producer ); + SMART_FIELD_INIT(HopEditor, label_product_id , lineEdit_product_id , Hop, PropertyNames::Hop::product_id ); + SMART_FIELD_INIT(HopEditor, label_year , lineEdit_year , Hop, PropertyNames::Hop::year ); + SMART_FIELD_INIT(HopEditor, label_total_oil_ml_per_100g, lineEdit_total_oil_ml_per_100g, Hop, PropertyNames::Hop::total_oil_ml_per_100g ); + SMART_FIELD_INIT(HopEditor, label_farnesene , lineEdit_farnesene , Hop, PropertyNames::Hop::farnesene_pct , 0); + SMART_FIELD_INIT(HopEditor, label_geraniol , lineEdit_geraniol , Hop, PropertyNames::Hop::geraniol_pct , 0); + SMART_FIELD_INIT(HopEditor, label_b_pinene , lineEdit_b_pinene , Hop, PropertyNames::Hop::b_pinene_pct , 0); + SMART_FIELD_INIT(HopEditor, label_linalool , lineEdit_linalool , Hop, PropertyNames::Hop::linalool_pct , 0); + SMART_FIELD_INIT(HopEditor, label_limonene , lineEdit_limonene , Hop, PropertyNames::Hop::limonene_pct , 0); + SMART_FIELD_INIT(HopEditor, label_nerol , lineEdit_nerol , Hop, PropertyNames::Hop::nerol_pct , 0); + SMART_FIELD_INIT(HopEditor, label_pinene , lineEdit_pinene , Hop, PropertyNames::Hop::pinene_pct , 0); + SMART_FIELD_INIT(HopEditor, label_polyphenols , lineEdit_polyphenols , Hop, PropertyNames::Hop::polyphenols_pct , 0); + SMART_FIELD_INIT(HopEditor, label_xanthohumol , lineEdit_xanthohumol , Hop, PropertyNames::Hop::xanthohumol_pct , 0); // // According to https://bugreports.qt.io/browse/QTBUG-50823 it is never going to be possible to specify the data (as diff --git a/src/HopSortFilterProxyModel.cpp b/src/HopSortFilterProxyModel.cpp index a9a6d7db..9bebb749 100644 --- a/src/HopSortFilterProxyModel.cpp +++ b/src/HopSortFilterProxyModel.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * HopSortFilterProxyModel.cpp is part of Brewken, and is copyright the following authors 2009-2021: + * HopSortFilterProxyModel.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Daniel Pettersson * • Mattias Måhl * • Matt Young @@ -28,27 +28,29 @@ #include "model/Hop.h" #include "tableModels/HopTableModel.h" -HopSortFilterProxyModel::HopSortFilterProxyModel(QObject *parent, bool filt) -: QSortFilterProxyModel(parent) -{ - filter = filt; +HopSortFilterProxyModel::HopSortFilterProxyModel(QObject *parent, bool filt) : + QSortFilterProxyModel(parent), + filter{filt} { + return; } bool HopSortFilterProxyModel::lessThan(QModelIndex const & left, QModelIndex const & right) const { QVariant leftHop = sourceModel()->data(left); QVariant rightHop = sourceModel()->data(right); + // .:TODO:. Change this to use Hop::useDisplayNames QStringList uses = QStringList() << "Dry Hop" << "Aroma" << "Boil" << "First Wort" << "Mash"; - switch (left.column()) { - case HOPALPHACOL: + auto const columnIndex = static_cast(left.column()); + switch (columnIndex) { + case HopTableModel::ColumnIndex::Alpha: { double lAlpha = Localization::toDouble(leftHop.toString(), Q_FUNC_INFO); double rAlpha = Localization::toDouble(rightHop.toString(), Q_FUNC_INFO); return lAlpha < rAlpha; } - case HOPINVENTORYCOL: + case HopTableModel::ColumnIndex::Inventory: if (Measurement::qStringToSI(leftHop.toString(), Measurement::PhysicalQuantity::Mass).quantity() == 0.0 && this->sortOrder() == Qt::AscendingOrder) { return false; @@ -56,35 +58,34 @@ bool HopSortFilterProxyModel::lessThan(QModelIndex const & left, return Measurement::qStringToSI(leftHop.toString(), Measurement::PhysicalQuantity::Mass) < Measurement::qStringToSI(rightHop.toString(), Measurement::PhysicalQuantity::Mass); - case HOPAMOUNTCOL: + case HopTableModel::ColumnIndex::Amount: return Measurement::qStringToSI(leftHop.toString(), Measurement::PhysicalQuantity::Mass) < Measurement::qStringToSI(rightHop.toString(), Measurement::PhysicalQuantity::Mass); - case HOPTIMECOL: + case HopTableModel::ColumnIndex::Time: { - // Get the indexes of the Use column - QModelIndex lSibling = left.sibling(left.row(), HOPUSECOL); - QModelIndex rSibling = right.sibling(right.row(), HOPUSECOL); - // We are talking to the model, so we get the strings associated with - // the names, not the Hop::Use enums. We need those translated into - // ints to make this work - int lUse = uses.indexOf( (sourceModel()->data(lSibling)).toString() ); - int rUse = uses.indexOf( (sourceModel()->data(rSibling)).toString() ); + // Get the indexes of the Use column + QModelIndex lSibling = left.sibling( left.row(), static_cast(HopTableModel::ColumnIndex::Use)); + QModelIndex rSibling = right.sibling(right.row(), static_cast(HopTableModel::ColumnIndex::Use)); + // We are talking to the model, so we get the strings associated with + // the names, not the Hop::Use enums. We need those translated into + // ints to make this work + int lUse = uses.indexOf( (sourceModel()->data(lSibling)).toString() ); + int rUse = uses.indexOf( (sourceModel()->data(rSibling)).toString() ); - if (lUse == rUse) { - return Measurement::qStringToSI(leftHop.toString(), Measurement::PhysicalQuantity::Time) < - Measurement::qStringToSI(rightHop.toString(), Measurement::PhysicalQuantity::Time); - } + if (lUse == rUse) { + return Measurement::qStringToSI(leftHop.toString(), Measurement::PhysicalQuantity::Time) < + Measurement::qStringToSI(rightHop.toString(), Measurement::PhysicalQuantity::Time); + } - return lUse < rUse; + return lUse < rUse; } } return leftHop.toString() < rightHop.toString(); } -bool HopSortFilterProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent) const -{ +bool HopSortFilterProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent) const { HopTableModel* model = qobject_cast(sourceModel()); QModelIndex index = sourceModel()->index(source_row, 0, source_parent); diff --git a/src/HopSortFilterProxyModel.h b/src/HopSortFilterProxyModel.h index 7e613d07..5bf78eb4 100644 --- a/src/HopSortFilterProxyModel.h +++ b/src/HopSortFilterProxyModel.h @@ -25,8 +25,7 @@ * * \brief Proxy model for sorting hops. */ -class HopSortFilterProxyModel : public QSortFilterProxyModel -{ +class HopSortFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: diff --git a/src/HydrometerTool.cpp b/src/HydrometerTool.cpp index 34c85922..5dc34aee 100644 --- a/src/HydrometerTool.cpp +++ b/src/HydrometerTool.cpp @@ -35,56 +35,62 @@ HydrometerTool::HydrometerTool(QWidget* parent) : QDialog(parent) { this->doLayout(); - connect(pushButton_convert, &QAbstractButton::clicked, this, &HydrometerTool::convert ); - connect(label_inputTemp, &BtLabel::changedSystemOfMeasurementOrScale, lineEdit_inputTemp, &BtAmountEdit::lineChanged); - connect(label_inputSg, &BtLabel::changedSystemOfMeasurementOrScale, lineEdit_inputSg, &BtAmountEdit::lineChanged); - connect(label_outputSg, &BtLabel::changedSystemOfMeasurementOrScale, lineEdit_outputSg, &BtAmountEdit::lineChanged); - connect(label_calibratedTemp, &BtLabel::changedSystemOfMeasurementOrScale, lineEdit_calibratedTemp, &BtAmountEdit::lineChanged); + SMART_FIELD_INIT_FS(HydrometerTool, label_inputSg , lineEdit_inputSg , double, Measurement::PhysicalQuantity::Density ); + SMART_FIELD_INIT_FS(HydrometerTool, label_outputSg , lineEdit_outputSg , double, Measurement::PhysicalQuantity::Density ); + SMART_FIELD_INIT_FS(HydrometerTool, label_calibratedTemp, lineEdit_calibratedTemp, double, Measurement::PhysicalQuantity::Temperature, 1); + SMART_FIELD_INIT_FS(HydrometerTool, label_inputTemp , lineEdit_inputTemp , double, Measurement::PhysicalQuantity::Temperature); + + connect(this->pushButton_convert, &QAbstractButton::clicked, this, &HydrometerTool::convert ); + connect(this->label_inputTemp, &SmartLabel::changedSystemOfMeasurementOrScale, this->lineEdit_inputTemp, &SmartLineEdit::lineChanged); + connect(this->label_inputSg, &SmartLabel::changedSystemOfMeasurementOrScale, this->lineEdit_inputSg, &SmartLineEdit::lineChanged); + connect(this->label_outputSg, &SmartLabel::changedSystemOfMeasurementOrScale, this->lineEdit_outputSg, &SmartLineEdit::lineChanged); + connect(this->label_calibratedTemp, &SmartLabel::changedSystemOfMeasurementOrScale, this->lineEdit_calibratedTemp, &SmartLineEdit::lineChanged); QMetaObject::connectSlotsByName(this); return; } void HydrometerTool::doLayout() { + // .:TBD:. We should either drop calls to setObjectName below or move thm into the init functions + resize(279, 96); QHBoxLayout* hLayout = new QHBoxLayout(this); QFormLayout* formLayout = new QFormLayout(); groupBox_inputSg = new QGroupBox(this); - groupBox_inputSg->setProperty(*PropertyNames::UiAmountWithUnits::configSection, QVariant(QStringLiteral("hydrometerTool"))); - label_inputSg = new BtDensityLabel(groupBox_inputSg); + label_inputSg = new SmartLabel(groupBox_inputSg); label_inputSg ->setContextMenuPolicy(Qt::CustomContextMenu); - lineEdit_inputSg = new BtDensityEdit(groupBox_inputSg); + lineEdit_inputSg = new SmartLineEdit(groupBox_inputSg); lineEdit_inputSg->setMinimumSize(QSize(80, 0)); lineEdit_inputSg->setMaximumSize(QSize(80, 16777215)); - label_inputTemp = new BtTemperatureLabel(groupBox_inputSg); + label_inputTemp = new SmartLabel(groupBox_inputSg); label_inputTemp ->setObjectName(QStringLiteral("label_inputTemp")); label_inputTemp ->setContextMenuPolicy(Qt::CustomContextMenu); - lineEdit_inputTemp = new BtTemperatureEdit(groupBox_inputSg); + lineEdit_inputTemp = new SmartLineEdit(groupBox_inputSg); lineEdit_inputTemp->setMinimumSize(QSize(80, 0)); lineEdit_inputTemp->setMaximumSize(QSize(80, 16777215)); lineEdit_inputTemp->setObjectName(QStringLiteral("lineEdit_inputTemp")); - label_calibratedTemp = new BtTemperatureLabel(groupBox_inputSg); + label_calibratedTemp = new SmartLabel(groupBox_inputSg); label_calibratedTemp ->setObjectName(QStringLiteral("label_calibratedTemp")); label_calibratedTemp ->setContextMenuPolicy(Qt::CustomContextMenu); - lineEdit_calibratedTemp = new BtTemperatureEdit(groupBox_inputSg); + lineEdit_calibratedTemp = new SmartLineEdit(groupBox_inputSg); lineEdit_calibratedTemp->setMinimumSize(QSize(80, 0)); lineEdit_calibratedTemp->setMaximumSize(QSize(80, 16777215)); lineEdit_calibratedTemp->setObjectName(QStringLiteral("lineEdit_calibratedTemp")); - lineEdit_calibratedTemp->setText(15.55555556,1); + lineEdit_calibratedTemp->setAmount(15.55555556); - label_outputSg = new BtDensityLabel(groupBox_inputSg); + label_outputSg = new SmartLabel(groupBox_inputSg); label_outputSg ->setContextMenuPolicy(Qt::CustomContextMenu); - lineEdit_outputSg = new BtDensityEdit(groupBox_inputSg); + lineEdit_outputSg = new SmartLineEdit(groupBox_inputSg); lineEdit_outputSg->setMinimumSize(QSize(80, 0)); lineEdit_outputSg->setMaximumSize(QSize(80, 16777215)); - lineEdit_outputSg->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); +/// lineEdit_outputSg->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); lineEdit_outputSg->setReadOnly(true); @@ -92,7 +98,7 @@ void HydrometerTool::doLayout() { label_inputSg->setBuddy(lineEdit_inputSg); label_inputTemp->setBuddy(lineEdit_inputTemp); label_outputSg->setBuddy(lineEdit_outputSg); -#endif //QT_NO_SHORTCUT +#endif formLayout->setWidget(0, QFormLayout::LabelRole, label_inputSg); formLayout->setWidget(0, QFormLayout::FieldRole, lineEdit_inputSg); @@ -127,28 +133,27 @@ void HydrometerTool::doLayout() { void HydrometerTool::retranslateUi() { setWindowTitle(tr("Hydrometer Tool")); - label_inputSg->setText(tr("SG Reading")); - label_inputTemp->setText(tr("Temperature")); + label_inputSg ->setText(tr("SG Reading")); + label_inputTemp ->setText(tr("Temperature")); label_calibratedTemp->setText(tr("Hydrometer Calibration")); - label_outputSg->setText(tr("Adjust SG")); - - pushButton_convert->setText(tr("Convert")); + label_outputSg ->setText(tr("Adjust SG")); + pushButton_convert ->setText(tr("Convert")); #ifndef QT_NO_TOOLTIP - lineEdit_inputSg->setToolTip(tr("Measured gravity")); + lineEdit_inputSg ->setToolTip(tr("Measured gravity")); lineEdit_inputTemp->setToolTip(tr("Temperature")); - lineEdit_outputSg->setToolTip(tr("Corrected gravity")); + lineEdit_outputSg ->setToolTip(tr("Corrected gravity")); #endif return; } void HydrometerTool::convert() { double correctedGravity = Algorithms::correctSgForTemperature( - lineEdit_inputSg->toCanonical().quantity(), // measured gravity - lineEdit_inputTemp->toCanonical().quantity(), // temperature at time of reading in Celsius + lineEdit_inputSg ->toCanonical().quantity(), // measured gravity + lineEdit_inputTemp ->toCanonical().quantity(), // temperature at time of reading in Celsius lineEdit_calibratedTemp->toCanonical().quantity() // calibration temperature of hydrometer in Celsius ); - lineEdit_outputSg->setText(correctedGravity); + lineEdit_outputSg->setAmount(correctedGravity); return; } diff --git a/src/HydrometerTool.h b/src/HydrometerTool.h index 391e45cd..83866dd3 100644 --- a/src/HydrometerTool.h +++ b/src/HydrometerTool.h @@ -21,9 +21,8 @@ #include -#include "BtAmountEdit.h" -#include "BtLabel.h" -#include "BtLineEdit.h" +#include "widgets/SmartLabel.h" +#include "widgets/SmartLineEdit.h" class QEvent; class QGroupBox; @@ -37,17 +36,17 @@ class HydrometerTool : public QDialog { //! \name Public UI Variables //! @{ - QPushButton * pushButton_convert; - BtDensityLabel * label_inputSg; - BtDensityEdit * lineEdit_inputSg; - BtDensityLabel * label_outputSg; - BtDensityEdit * lineEdit_outputSg; - - BtTemperatureLabel * label_inputTemp; - BtTemperatureEdit * lineEdit_inputTemp; - BtTemperatureLabel * label_calibratedTemp; - BtTemperatureEdit * lineEdit_calibratedTemp; - QGroupBox * groupBox_inputSg; + QPushButton * pushButton_convert; + SmartLabel * label_inputSg; + SmartLineEdit * lineEdit_inputSg; + SmartLabel * label_outputSg; + SmartLineEdit * lineEdit_outputSg; + + SmartLabel * label_inputTemp; + SmartLineEdit * lineEdit_inputTemp; + SmartLabel * label_calibratedTemp; + SmartLineEdit * lineEdit_calibratedTemp; + QGroupBox * groupBox_inputSg; //! @} public slots: diff --git a/src/InventoryFormatter.cpp b/src/InventoryFormatter.cpp index 5486d97f..ddf1fb12 100644 --- a/src/InventoryFormatter.cpp +++ b/src/InventoryFormatter.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * InventoryFormatter.cpp is part of Brewken, and is copyright the following authors 2016-2022: + * InventoryFormatter.cpp is part of Brewken, and is copyright the following authors 2016-2023: * • Mark de Wever * • Mattias Måhl * • Matt Young @@ -72,9 +72,8 @@ namespace { "%2" "") .arg(fermentable->name()) - .arg(Measurement::displayAmount(Measurement::Amount{fermentable->inventory(), Measurement::Units::kilograms}, - PersistentSettings::Sections::fermentableTable, - PropertyNames::NamedEntityWithInventory::inventory)); + .arg(Measurement::displayAmount(Measurement::Amount{fermentable->inventory(), + Measurement::Units::kilograms})); } result += ""; } @@ -111,9 +110,8 @@ namespace { "") .arg(hop->name()) .arg(hop->alpha_pct()) - .arg(Measurement::displayAmount(Measurement::Amount{hop->inventory(), Measurement::Units::kilograms}, - PersistentSettings::Sections::hopTable, - PropertyNames::NamedEntityWithInventory::inventory)); + .arg(Measurement::displayAmount(Measurement::Amount{hop->inventory(), + Measurement::Units::kilograms})); } result += ""; } @@ -145,9 +143,7 @@ namespace { Measurement::Amount{ miscellaneous->inventory(), miscellaneous->amountIsWeight() ? Measurement::Units::kilograms : Measurement::Units::liters - }, - PersistentSettings::Sections::miscTable, - PropertyNames::NamedEntityWithInventory::inventory + } ); result += QString("" "%1" @@ -184,9 +180,7 @@ namespace { Measurement::Amount{ yeast->inventory(), yeast->amountIsWeight() ? Measurement::Units::kilograms : Measurement::Units::liters - }, - PersistentSettings::Sections::yeastTable, - PropertyNames::NamedEntityWithInventory::inventory + } ); result += QString("" diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 7082cf23..e6041d6f 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -176,6 +176,45 @@ namespace { mainWindowInstance = new MainWindow(); return; } + + /** + * + */ + void updateDensitySlider(RangedSlider & slider, + SmartLabel const & label, + double const minCanonicalValue, + double const maxCanonicalValue, + double const maxPossibleCanonicalValue) { + slider.setPreferredRange(label.getRangeToDisplay(minCanonicalValue, maxCanonicalValue )); + slider.setRange (label.getRangeToDisplay(1.000 , maxPossibleCanonicalValue)); + + Measurement::UnitSystem const & displayUnitSystem = label.getDisplayUnitSystem(); + if (displayUnitSystem == Measurement::UnitSystems::density_Plato) { + slider.setPrecision(1); + slider.setTickMarks(2, 5); + } else { + slider.setPrecision(3); + slider.setTickMarks(0.010, 2); + } + return; + } + + /** + * + */ + void updateColorSlider(RangedSlider & slider, + SmartLabel const & label, + double const minCanonicalValue, + double const maxCanonicalValue) { + slider.setPreferredRange(label.getRangeToDisplay(minCanonicalValue, maxCanonicalValue)); + slider.setRange (label.getRangeToDisplay(1 , 44 )); + + Measurement::UnitSystem const & displayUnitSystem = label.getDisplayUnitSystem(); + slider.setTickMarks(displayUnitSystem == Measurement::UnitSystems::color_StandardReferenceMethod ? 10 : 40, 2); + + return; + } + } // This private implementation class holds all private non-virtual members of MainWindow @@ -190,10 +229,11 @@ class MainWindow::impl { ~impl() = default; - // TODO Try making this a smart pointer HelpDialog * helpDialog; + + private: MainWindow & self; QFileDialog* fileOpener; @@ -285,10 +325,17 @@ void MainWindow::init() { Mash::connectSignals(); qDebug() << Q_FUNC_INFO << "Mash signals connected"; + // .:TBD:. We should fix these inconsistently-named labels + SMART_FIELD_INIT(MainWindow, label_boilSg , lineEdit_boilSg , Recipe, PropertyNames::Recipe::boilGrav ); + SMART_FIELD_INIT(MainWindow, label_boiltime , lineEdit_boilTime , Recipe, PropertyNames::Recipe::boilTime_min ); + SMART_FIELD_INIT(MainWindow, efficiencyLabel , lineEdit_efficiency, Recipe, PropertyNames::Recipe::efficiency_pct); + SMART_FIELD_INIT(MainWindow, label_targetBatchSize, lineEdit_batchSize , Recipe, PropertyNames::Recipe::batchSize_l ); + SMART_FIELD_INIT(MainWindow, label_boilsize , lineEdit_boilSize , Recipe, PropertyNames::Recipe::boilSize_l ); + // I do not like this connection here. - connect(ancestorDialog, &AncestorDialog::ancestoryChanged, treeView_recipe->model(), &BtTreeModel::versionedRecipe); - connect(optionDialog, &OptionDialog::showAllAncestors, treeView_recipe->model(), &BtTreeModel::catchAncestors); - connect(treeView_recipe, &BtTreeView::recipeSpawn, this, &MainWindow::versionedRecipe); + connect(this->ancestorDialog, &AncestorDialog::ancestoryChanged, treeView_recipe->model(), &BtTreeModel::versionedRecipe); + connect(this->optionDialog, &OptionDialog::showAllAncestors, treeView_recipe->model(), &BtTreeModel::catchAncestors ); + connect(this->treeView_recipe, &BtTreeView::recipeSpawn, this, &MainWindow::versionedRecipe ); // No connections from the database yet? Oh FSM, that probably means I'm // doing it wrong again. @@ -430,52 +477,48 @@ void MainWindow::setupCSS() // Most dialogs are initialized in here. That should include any initial // configurations as well -void MainWindow::setupDialogs() -{ - dialog_about = new AboutDialog(this); - this->pimpl->helpDialog = new HelpDialog(this); - equipEditor = new EquipmentEditor(this); - singleEquipEditor = new EquipmentEditor(this, true); - fermDialog = new FermentableDialog(this); - fermEditor = new FermentableEditor(this); - hopDialog = new HopDialog(this); - hopEditor = new HopEditor(this); - mashEditor = new MashEditor(this); - mashStepEditor = new MashStepEditor(this); - mashWizard = new MashWizard(this); - miscDialog = new MiscDialog(this); - miscEditor = new MiscEditor(this); - styleEditor = new StyleEditor(this); - singleStyleEditor = new StyleEditor(this,true); - yeastDialog = new YeastDialog(this); - yeastEditor = new YeastEditor(this); - optionDialog = new OptionDialog(this); - recipeScaler = new ScaleRecipeTool(this); - recipeFormatter = new RecipeFormatter(this); - printAndPreviewDialog = new PrintAndPreviewDialog(this); - ogAdjuster = new OgAdjuster(this); - converterTool = new ConverterTool(this); - hydrometerTool = new HydrometerTool(this); - alcoholTool = new AlcoholTool(this); - timerMainDialog = new TimerMainDialog(this); - primingDialog = new PrimingDialog(this); - strikeWaterDialog = new StrikeWaterDialog(this); - refractoDialog = new RefractoDialog(this); - mashDesigner = new MashDesigner(this); - pitchDialog = new PitchDialog(this); - btDatePopup = new BtDatePopup(this); - - waterDialog = new WaterDialog(this); - waterEditor = new WaterEditor(this); - - ancestorDialog = new AncestorDialog(this); - +void MainWindow::setupDialogs() { + this->dialog_about = new AboutDialog(this); + this->pimpl->helpDialog = new HelpDialog(this); + this->equipEditor = new EquipmentEditor(this); + this->singleEquipEditor = new EquipmentEditor(this, true); + this->fermDialog = new FermentableDialog(this); + this->fermEditor = new FermentableEditor(this); + this->hopDialog = new HopDialog(this); + this->hopEditor = new HopEditor(this); + this->mashEditor = new MashEditor(this); + this->mashStepEditor = new MashStepEditor(this); + this->mashWizard = new MashWizard(this); + this->miscDialog = new MiscDialog(this); + this->miscEditor = new MiscEditor(this); + this->styleEditor = new StyleEditor(this); + this->singleStyleEditor = new StyleEditor(this,true); + this->yeastDialog = new YeastDialog(this); + this->yeastEditor = new YeastEditor(this); + this->optionDialog = new OptionDialog(this); + this->recipeScaler = new ScaleRecipeTool(this); + this->recipeFormatter = new RecipeFormatter(this); + this->printAndPreviewDialog = new PrintAndPreviewDialog(this); + this->ogAdjuster = new OgAdjuster(this); + this->converterTool = new ConverterTool(this); + this->hydrometerTool = new HydrometerTool(this); + this->alcoholTool = new AlcoholTool(this); + this->timerMainDialog = new TimerMainDialog(this); + this->primingDialog = new PrimingDialog(this); + this->strikeWaterDialog = new StrikeWaterDialog(this); + this->refractoDialog = new RefractoDialog(this); + this->mashDesigner = new MashDesigner(this); + this->pitchDialog = new PitchDialog(this); + this->btDatePopup = new BtDatePopup(this); + this->waterDialog = new WaterDialog(this); + this->waterEditor = new WaterEditor(this); + this->ancestorDialog = new AncestorDialog(this); + return; } // Configures the range widgets for the bubbles -void MainWindow::setupRanges() -{ +void MainWindow::setupRanges() { styleRangeWidget_og->setRange(1.000, 1.120); styleRangeWidget_og->setPrecision(3); styleRangeWidget_og->setTickMarks(0.010, 2); @@ -494,13 +537,13 @@ void MainWindow::setupRanges() // definitely cheating, but I don't feel like making a whole subclass just to support this // or the next. - rangeWidget_batchsize->setRange(0, recipeObs == nullptr ? 19.0 : recipeObs->batchSize_l()); - rangeWidget_batchsize->setPrecision(1); - rangeWidget_batchsize->setTickMarks(2,5); + rangeWidget_batchSize->setRange(0, recipeObs == nullptr ? 19.0 : recipeObs->batchSize_l()); + rangeWidget_batchSize->setPrecision(1); + rangeWidget_batchSize->setTickMarks(2,5); - rangeWidget_batchsize->setBackgroundBrush(QColor(255,255,255)); - rangeWidget_batchsize->setPreferredRangeBrush(QColor(55,138,251)); - rangeWidget_batchsize->setMarkerBrush(QBrush(Qt::NoBrush)); + rangeWidget_batchSize->setBackgroundBrush(QColor(255,255,255)); + rangeWidget_batchSize->setPreferredRangeBrush(QColor(55,138,251)); + rangeWidget_batchSize->setMarkerBrush(QBrush(Qt::NoBrush)); rangeWidget_boilsize->setRange(0, recipeObs == nullptr? 24.0 : recipeObs->boilVolume_l()); rangeWidget_boilsize->setPrecision(1); @@ -632,16 +675,16 @@ void MainWindow::setupTables() }); // Enable sorting in the main tables. - fermentableTable->horizontalHeader()->setSortIndicator( FERMAMOUNTCOL, Qt::DescendingOrder ); + fermentableTable->horizontalHeader()->setSortIndicator(static_cast(FermentableTableModel::ColumnIndex::Amount), Qt::DescendingOrder ); fermentableTable->setSortingEnabled(true); fermTableProxy->setDynamicSortFilter(true); - hopTable->horizontalHeader()->setSortIndicator( HOPTIMECOL, Qt::DescendingOrder ); + hopTable->horizontalHeader()->setSortIndicator(static_cast(HopTableModel::ColumnIndex::Time), Qt::DescendingOrder ); hopTable->setSortingEnabled(true); hopTableProxy->setDynamicSortFilter(true); - miscTable->horizontalHeader()->setSortIndicator( MISCUSECOL, Qt::DescendingOrder ); + miscTable->horizontalHeader()->setSortIndicator(static_cast(MiscTableModel::ColumnIndex::Use), Qt::DescendingOrder ); miscTable->setSortingEnabled(true); miscTableProxy->setDynamicSortFilter(true); - yeastTable->horizontalHeader()->setSortIndicator( YEASTNAMECOL, Qt::DescendingOrder ); + yeastTable->horizontalHeader()->setSortIndicator(static_cast(YeastTableModel::ColumnIndex::Name), Qt::DescendingOrder ); yeastTable->setSortingEnabled(true); yeastTableProxy->setDynamicSortFilter(true); } @@ -843,20 +886,20 @@ void MainWindow::setupActivate() { // lineEdits with either an editingFinished() or a textModified() should go in // here void MainWindow::setupTextEdit() { - connect(this->lineEdit_name, &QLineEdit::editingFinished, this, &MainWindow::updateRecipeName); - connect(this->lineEdit_batchSize, &BtLineEdit::textModified, this, &MainWindow::updateRecipeBatchSize); - connect(this->lineEdit_boilSize, &BtLineEdit::textModified, this, &MainWindow::updateRecipeBoilSize); - connect(this->lineEdit_boilTime, &BtLineEdit::textModified, this, &MainWindow::updateRecipeBoilTime); - connect(this->lineEdit_efficiency, &BtLineEdit::textModified, this, &MainWindow::updateRecipeEfficiency); + connect(this->lineEdit_name, &QLineEdit::editingFinished, this, &MainWindow::updateRecipeName); + connect(this->lineEdit_batchSize, &SmartLineEdit::textModified, this, &MainWindow::updateRecipeBatchSize); + connect(this->lineEdit_boilSize, &SmartLineEdit::textModified, this, &MainWindow::updateRecipeBoilSize); + connect(this->lineEdit_boilTime, &SmartLineEdit::textModified, this, &MainWindow::updateRecipeBoilTime); + connect(this->lineEdit_efficiency, &SmartLineEdit::textModified, this, &MainWindow::updateRecipeEfficiency); return; } -// anything using a BtLabel::changedSystemOfMeasurementOrScale signal should go in here +// anything using a SmartLabel::changedSystemOfMeasurementOrScale signal should go in here void MainWindow::setupLabels() { // These are the sliders. I need to consider these harder, but small steps - connect(this->oGLabel, &BtLabel::changedSystemOfMeasurementOrScale, this, &MainWindow::redisplayLabel); - connect(this->fGLabel, &BtLabel::changedSystemOfMeasurementOrScale, this, &MainWindow::redisplayLabel); - connect(this->colorSRMLabel, &BtLabel::changedSystemOfMeasurementOrScale, this, &MainWindow::redisplayLabel); + connect(this->oGLabel, &SmartLabel::changedSystemOfMeasurementOrScale, this, &MainWindow::redisplayLabel); + connect(this->fGLabel, &SmartLabel::changedSystemOfMeasurementOrScale, this, &MainWindow::redisplayLabel); + connect(this->colorSRMLabel, &SmartLabel::changedSystemOfMeasurementOrScale, this, &MainWindow::redisplayLabel); return; } @@ -1287,60 +1330,6 @@ void MainWindow::changed(QMetaProperty prop, QVariant val) { return; } -void MainWindow::updateDensitySlider(BtStringConst const & propertyNameMin, - BtStringConst const & propertyNameMax, - BtStringConst const & propertyNameCurrent, - RangedSlider* slider, - double max) { - Measurement::UnitSystem const & displayUnitSystem = - Measurement::getUnitSystemForField(*propertyNameCurrent, - *PersistentSettings::Sections::tab_recipe, - Measurement::PhysicalQuantity::Density); - slider->setPreferredRange(Measurement::displayRange(recStyle, - this->tab_recipe, - propertyNameMin, - propertyNameMax, - &Measurement::Units::sp_grav)); - slider->setRange(Measurement::displayRange(this->tab_recipe, - propertyNameCurrent, - 1.000, - max, - Measurement::Units::sp_grav)); - - if (displayUnitSystem == Measurement::UnitSystems::density_Plato) { - slider->setPrecision(1); - slider->setTickMarks(2,5); - } else { - slider->setPrecision(3); - slider->setTickMarks(0.010, 2); - } - return; -} - -void MainWindow::updateColorSlider(BtStringConst const & propertyNameMin, - BtStringConst const & propertyNameMax, - BtStringConst const & propertyNameCurrent, - RangedSlider * slider) { - Measurement::UnitSystem const & displayUnitSystem = - Measurement::getUnitSystemForField(*propertyNameCurrent, - *PersistentSettings::Sections::tab_recipe, - Measurement::PhysicalQuantity::Color); - - slider->setPreferredRange(Measurement::displayRange(recStyle, - this->tab_recipe, - propertyNameMin, - propertyNameMax, - &Measurement::Units::srm)); - slider->setRange(Measurement::displayRange(this->tab_recipe, - propertyNameCurrent, - 1, - 44, - Measurement::Units::srm)); - slider->setTickMarks(displayUnitSystem == Measurement::UnitSystems::color_StandardReferenceMethod ? 10 : 40, 2); - - return; -} - void MainWindow::showChanges(QMetaProperty* prop) { if (recipeObs == nullptr) { return; @@ -1354,16 +1343,16 @@ void MainWindow::showChanges(QMetaProperty* prop) { } // May St. Stevens preserve me - lineEdit_name ->setText(recipeObs->name ()); - lineEdit_batchSize ->setText(recipeObs->batchSize_l ()); - lineEdit_boilSize ->setText(recipeObs->boilSize_l ()); - lineEdit_efficiency->setText(recipeObs->efficiency_pct()); - lineEdit_boilTime ->setText(recipeObs->boilTime_min ()); - lineEdit_name ->setCursorPosition(0); - lineEdit_batchSize ->setCursorPosition(0); - lineEdit_boilSize ->setCursorPosition(0); - lineEdit_efficiency->setCursorPosition(0); - lineEdit_boilTime ->setCursorPosition(0); + this->lineEdit_name ->setText (this->recipeObs->name ()); + this->lineEdit_batchSize ->setAmount(this->recipeObs->batchSize_l ()); + this->lineEdit_boilSize ->setAmount(this->recipeObs->boilSize_l ()); + this->lineEdit_efficiency->setAmount(this->recipeObs->efficiency_pct()); + this->lineEdit_boilTime ->setAmount(this->recipeObs->boilTime_min ()); + this->lineEdit_name ->setCursorPosition(0); + this->lineEdit_batchSize ->setCursorPosition(0); + this->lineEdit_boilSize ->setCursorPosition(0); + this->lineEdit_efficiency->setCursorPosition(0); + this->lineEdit_boilTime ->setCursorPosition(0); /* lineEdit_calcBatchSize->setText(recipeObs); lineEdit_calcBoilSize->setText(recipeObs); @@ -1385,31 +1374,37 @@ void MainWindow::showChanges(QMetaProperty* prop) { else lineEdit_calcBoilSize->setStyleSheet(highSS); */ - lineEdit_boilSg->setText(recipeObs); + this->lineEdit_boilSg->setAmount(this->recipeObs->boilGrav()); + + Style const * style = this->recipeObs->style(); - updateDensitySlider(PropertyNames::Style::ogMin, PropertyNames::Style::ogMax, PropertyNames::Recipe::og, styleRangeWidget_og, 1.120); - styleRangeWidget_og->setValue(Measurement::amountDisplay(recipeObs, tab_recipe, PropertyNames::Recipe::og, &Measurement::Units::sp_grav)); + updateDensitySlider(*this->styleRangeWidget_og, this->oGLabel, style->ogMin(), style->ogMax(), 1.120); + this->styleRangeWidget_og->setValue(this->oGLabel->getAmountToDisplay(recipeObs->og())); - updateDensitySlider(PropertyNames::Style::fgMin, PropertyNames::Style::fgMax, PropertyNames::Recipe::fg, styleRangeWidget_fg, 1.03); - styleRangeWidget_fg->setValue(Measurement::amountDisplay(recipeObs, tab_recipe, PropertyNames::Recipe::fg, &Measurement::Units::sp_grav)); + updateDensitySlider(*this->styleRangeWidget_fg, this->fGLabel, style->fgMin(), style->fgMax(), 1.030); + this->styleRangeWidget_fg->setValue(this->fGLabel->getAmountToDisplay(recipeObs->fg())); - styleRangeWidget_abv->setValue(recipeObs->ABV_pct()); - styleRangeWidget_ibu->setValue(recipeObs->IBU()); + this->styleRangeWidget_abv->setValue(recipeObs->ABV_pct()); + this->styleRangeWidget_ibu->setValue(recipeObs->IBU()); - rangeWidget_batchsize->setRange(0, Measurement::amountDisplay(recipeObs, tab_recipe, PropertyNames::Recipe::batchSize_l, &Measurement::Units::liters)); - rangeWidget_batchsize->setPreferredRange(0, Measurement::amountDisplay(recipeObs, tab_recipe, PropertyNames::Recipe::finalVolume_l, &Measurement::Units::liters)); - rangeWidget_batchsize->setValue(Measurement::amountDisplay(recipeObs, tab_recipe, PropertyNames::Recipe::finalVolume_l, &Measurement::Units::liters)); + this->rangeWidget_batchSize->setRange (0, + this->label_batchSize->getAmountToDisplay(this->recipeObs->batchSize_l())); + this->rangeWidget_batchSize->setPreferredRange(0, + this->label_batchSize->getAmountToDisplay(this->recipeObs->finalVolume_l())); + this->rangeWidget_batchSize->setValue (this->label_batchSize->getAmountToDisplay(this->recipeObs->finalVolume_l())); - rangeWidget_boilsize->setRange(0, Measurement::amountDisplay(recipeObs, tab_recipe, PropertyNames::Recipe::boilSize_l, &Measurement::Units::liters)); - rangeWidget_boilsize->setPreferredRange(0, Measurement::amountDisplay(recipeObs, tab_recipe, PropertyNames::Recipe::boilVolume_l, &Measurement::Units::liters)); - rangeWidget_boilsize->setValue(Measurement::amountDisplay(recipeObs, tab_recipe, PropertyNames::Recipe::boilVolume_l, &Measurement::Units::liters)); + this->rangeWidget_boilsize->setRange (0, + this->label_boilsize->getAmountToDisplay(this->recipeObs->boilSize_l())); + this->rangeWidget_boilsize->setPreferredRange(0, + this->label_boilsize->getAmountToDisplay(this->recipeObs->boilVolume_l())); + this->rangeWidget_boilsize->setValue (this->label_boilsize->getAmountToDisplay(this->recipeObs->boilVolume_l())); /* Colors need the same basic treatment as gravity */ - updateColorSlider(PropertyNames::Style::colorMin_srm, - PropertyNames::Style::colorMax_srm, - PropertyNames::Recipe::color_srm, - styleRangeWidget_srm); - styleRangeWidget_srm->setValue(Measurement::amountDisplay(recipeObs, tab_recipe, PropertyNames::Recipe::color_srm, &Measurement::Units::srm)); + updateColorSlider(*this->styleRangeWidget_srm, + this->colorSRMLabel, + style->colorMin_srm(), + style->colorMax_srm()); + this->styleRangeWidget_srm->setValue(this->colorSRMLabel->getAmountToDisplay(this->recipeObs->color_srm())); // In some, incomplete, recipes, OG is approximately 1.000, which then makes GU close to 0 and thus IBU/GU insanely // large. Besides being meaningless, such a large number takes up a lot of space. So, where gravity units are @@ -1458,25 +1453,14 @@ void MainWindow::displayRangesEtcForCurrentRecipeStyle() { return; } - styleRangeWidget_og->setPreferredRange(Measurement::displayRange(style, - this->tab_recipe, - PropertyNames::Style::ogMin, - PropertyNames::Style::ogMax, - &Measurement::Units::sp_grav)); - styleRangeWidget_fg->setPreferredRange(Measurement::displayRange(style, - this->tab_recipe, - PropertyNames::Style::fgMin, - PropertyNames::Style::fgMax, - &Measurement::Units::sp_grav)); + this->styleRangeWidget_og->setPreferredRange(this->oGLabel->getRangeToDisplay(style->ogMin(), style->ogMax())); - styleRangeWidget_abv->setPreferredRange(style->abvMin_pct(), style->abvMax_pct()); - styleRangeWidget_ibu->setPreferredRange(style->ibuMin(), style->ibuMax()); - styleRangeWidget_srm->setPreferredRange(Measurement::displayRange(style, - this->tab_recipe, - PropertyNames::Style::colorMin_srm, - PropertyNames::Style::colorMax_srm, - &Measurement::Units::srm)); + this->styleRangeWidget_fg->setPreferredRange(this->fGLabel->getRangeToDisplay(style->ogMin(), style->ogMax())); + this->styleRangeWidget_abv->setPreferredRange(style->abvMin_pct(), style->abvMax_pct()); + this->styleRangeWidget_ibu->setPreferredRange(style->ibuMin(), style->ibuMax()); + this->styleRangeWidget_srm->setPreferredRange(this->colorSRMLabel->getRangeToDisplay(style->colorMin_srm(), + style->colorMax_srm())); this->styleButton->setStyle(style); return; diff --git a/src/MainWindow.h b/src/MainWindow.h index 8e6d889e..2259d11c 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * MainWindow.h is part of Brewken, and is copyright the following authors 2009-2022: + * MainWindow.h is part of Brewken, and is copyright the following authors 2009-2023: * • Aidan Roberts * • Dan Cavanagh * • Daniel Pettersson @@ -463,16 +463,6 @@ private slots: //! \brief Connect signal/slots for check boxes void setUpStateChanges(); - void updateDensitySlider(BtStringConst const & propertyNameMin, - BtStringConst const & propertyNameMax, - BtStringConst const & propertyNameCurrent, - RangedSlider* slider, - double max); - void updateColorSlider(BtStringConst const & propertyNameMin, - BtStringConst const & propertyNameMax, - BtStringConst const & propertyNameCurrent, - RangedSlider* slider); - }; #endif diff --git a/src/MashDesigner.cpp b/src/MashDesigner.cpp index 4134e08a..16452e1f 100644 --- a/src/MashDesigner.cpp +++ b/src/MashDesigner.cpp @@ -41,34 +41,37 @@ MashDesigner::MashDesigner(QWidget * parent) : QDialog {parent}, addedWater_l{0} { this->setupUi(this); - this->label_zeroVol->setText(Measurement::displayAmount(Measurement::Amount{0, Measurement::Units::liters})); + this->label_zeroVol ->setText(Measurement::displayAmount(Measurement::Amount{0, Measurement::Units::liters})); this->label_zeroWort->setText(Measurement::displayAmount(Measurement::Amount{0, Measurement::Units::liters})); + SMART_FIELD_INIT_FS(MashDesigner, label_temp, lineEdit_temp, double, Measurement::PhysicalQuantity::Temperature, 1); // Target temp. + SMART_FIELD_INIT_FS(MashDesigner, label_stepTime, lineEdit_time, double, Measurement::PhysicalQuantity::Time, 0); // Time + // Update temp slider when we move amount slider. connect(horizontalSlider_amount, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateTempSlider); // Update amount slider when we move temp slider. - connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateAmtSlider); + connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateAmtSlider); // Update tun fullness bar when either slider moves. connect(horizontalSlider_amount, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateFullness); - connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateFullness); + connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateFullness); // Update amount/temp text when sliders move. connect(horizontalSlider_amount, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateAmt); connect(horizontalSlider_amount, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateTemp); - connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateAmt); - connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateTemp); + connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateAmt); + connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateTemp); // Update collected wort when sliders move. connect(horizontalSlider_amount, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateCollectedWort); - connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateCollectedWort); + connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateCollectedWort); // Save the target temp whenever it's changed. - connect(lineEdit_temp, &BtLineEdit::textModified, this, &MashDesigner::saveTargetTemp); + connect(lineEdit_temp, &SmartLineEdit::textModified, this, &MashDesigner::saveTargetTemp); // Move to next step. - connect(pushButton_next, &QAbstractButton::clicked, this, &MashDesigner::proceed); + connect(pushButton_next, &QAbstractButton::clicked, this, &MashDesigner::proceed); // Do correct calcs when the mash step type is selected. connect(comboBox_type, static_cast(&QComboBox::activated), this, &MashDesigner::typeChanged); // I still dislike this part. But I also need to "fix" the form // connect(checkBox_batchSparge, SIGNAL(clicked()), this, SLOT(updateMaxAmt())); - connect(pushButton_finish, &QAbstractButton::clicked, this, &MashDesigner::saveAndClose); + connect(pushButton_finish, &QAbstractButton::clicked, this, &MashDesigner::saveAndClose); return; } @@ -587,7 +590,7 @@ void MashDesigner::saveTargetTemp() { double temp = this->bound_temp_c(this->stepTemp_c()); // be nice and reset the field so it displays in proper units - this->lineEdit_temp->setText(temp); + this->lineEdit_temp->setAmount(temp); if (this->mashStep) { this->mashStep->setStepTemp_c(temp); } diff --git a/src/MashEditor.cpp b/src/MashEditor.cpp index db95bfa2..b0b10123 100644 --- a/src/MashEditor.cpp +++ b/src/MashEditor.cpp @@ -31,13 +31,13 @@ MashEditor::MashEditor(QWidget* parent) : QDialog(parent), mashObs(nullptr) { setupUi(this); - SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_name , PropertyNames::NamedEntity::name ); - SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_grainTemp , PropertyNames::Mash::grainTemp_c , *this->label_grainTemp , 1); - SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_spargeTemp, PropertyNames::Mash::spargeTemp_c , *this->label_spargeTemp, 1); - SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_spargePh , PropertyNames::Mash::ph , *this->label_spargePh , 0); - SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_tunTemp , PropertyNames::Mash::tunTemp_c , *this->label_tunTemp , 1); - SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_tunMass , PropertyNames::Mash::tunWeight_kg , *this->label_tunMass ); - SMART_LINE_EDIT_INIT(MashEditor, Mash, lineEdit_tunSpHeat , PropertyNames::Mash::tunSpecificHeat_calGC, *this->label_tunSpHeat , 1); + SMART_FIELD_INIT(MashEditor, label_name , lineEdit_name , Mash, PropertyNames::NamedEntity::name ); + SMART_FIELD_INIT(MashEditor, label_grainTemp , lineEdit_grainTemp , Mash, PropertyNames::Mash::grainTemp_c , 1); + SMART_FIELD_INIT(MashEditor, label_spargeTemp, lineEdit_spargeTemp, Mash, PropertyNames::Mash::spargeTemp_c , 1); + SMART_FIELD_INIT(MashEditor, label_spargePh , lineEdit_spargePh , Mash, PropertyNames::Mash::ph , 0); + SMART_FIELD_INIT(MashEditor, label_tunTemp , lineEdit_tunTemp , Mash, PropertyNames::Mash::tunTemp_c , 1); + SMART_FIELD_INIT(MashEditor, label_tunMass , lineEdit_tunMass , Mash, PropertyNames::Mash::tunWeight_kg ); + SMART_FIELD_INIT(MashEditor, label_tunSpHeat , lineEdit_tunSpHeat , Mash, PropertyNames::Mash::tunSpecificHeat_calGC, 1); connect(pushButton_fromEquipment, &QAbstractButton::clicked, this, &MashEditor::fromEquipment); connect(this, &QDialog::accepted, this, &MashEditor::saveAndClose ); diff --git a/src/MashStepEditor.cpp b/src/MashStepEditor.cpp index 23806b01..e623af6b 100644 --- a/src/MashStepEditor.cpp +++ b/src/MashStepEditor.cpp @@ -27,8 +27,16 @@ MashStepEditor::MashStepEditor(QWidget* parent) : QDialog{parent}, obs(nullptr) this->comboBox_type->setCurrentIndex(-1); - connect(buttonBox, &QDialogButtonBox::accepted, this, &MashStepEditor::saveAndClose); - connect(buttonBox, &QDialogButtonBox::rejected, this, &MashStepEditor::close); + SMART_FIELD_INIT(MashStepEditor, label_stepTemp , lineEdit_stepTemp , MashStep, PropertyNames::MashStep::stepTemp_c , 1); + SMART_FIELD_INIT(MashStepEditor, label_infuseAmount , lineEdit_infuseAmount , MashStep, PropertyNames::MashStep::infuseAmount_l ); + SMART_FIELD_INIT(MashStepEditor, label_infuseTemp , lineEdit_infuseTemp , MashStep, PropertyNames::MashStep::infuseTemp_c , 1); + SMART_FIELD_INIT(MashStepEditor, label_decoctionAmount, lineEdit_decoctionAmount, MashStep, PropertyNames::MashStep::decoctionAmount_l ); + SMART_FIELD_INIT(MashStepEditor, label_stepTime , lineEdit_stepTime , MashStep, PropertyNames::MashStep::stepTime_min , 0); + SMART_FIELD_INIT(MashStepEditor, label_rampTime , lineEdit_rampTime , MashStep, PropertyNames::MashStep::rampTime_min , 0); + SMART_FIELD_INIT(MashStepEditor, label_endTemp , lineEdit_endTemp , MashStep, PropertyNames::MashStep::endTemp_c , 1); + + connect(this->buttonBox, &QDialogButtonBox::accepted, this, &MashStepEditor::saveAndClose); + connect(this->buttonBox, &QDialogButtonBox::rejected, this, &MashStepEditor::close ); connect(this->comboBox_type, &QComboBox::currentTextChanged, this, &MashStepEditor::grayOutStuff); return; } @@ -50,48 +58,28 @@ void MashStepEditor::showChanges(QMetaProperty* metaProp) { value = metaProp->read(this->obs.get()); } - if (updateAll) { - lineEdit_name->setText(obs->name()); - comboBox_type->setCurrentIndex(static_cast(obs->type())); - lineEdit_infuseAmount->setText(this->obs.get()); - lineEdit_infuseTemp->setText(this->obs.get()); - lineEdit_decoctionAmount->setText(this->obs.get()); - lineEdit_stepTemp->setText(this->obs.get()); - lineEdit_stepTime->setText(this->obs.get()); - lineEdit_rampTime->setText(this->obs.get()); - lineEdit_endTemp->setText(this->obs.get()); - } else if (propName == PropertyNames::NamedEntity::name) { - lineEdit_name->setText(obs->name()); - } else if (propName == PropertyNames::MashStep::type) { - comboBox_type->setCurrentIndex(static_cast(obs->type())); - } else if (propName == PropertyNames::MashStep::infuseAmount_l) { - lineEdit_infuseAmount->setText(this->obs.get()); - } else if (propName == PropertyNames::MashStep::infuseTemp_c) { - lineEdit_infuseTemp->setText(this->obs.get()); - } else if (propName == PropertyNames::MashStep::decoctionAmount_l) { - lineEdit_decoctionAmount->setText(this->obs.get()); - } else if (propName == PropertyNames::MashStep::stepTemp_c) { - lineEdit_stepTemp->setText(this->obs.get()); - } else if (propName == PropertyNames::MashStep::stepTime_min) { - lineEdit_stepTime->setText(this->obs.get()); - } else if (propName == PropertyNames::MashStep::rampTime_min) { - lineEdit_rampTime->setText(this->obs.get()); - } else if (propName == PropertyNames::MashStep::endTemp_c) { - lineEdit_endTemp->setText(this->obs.get()); - } + if (updateAll || propName == PropertyNames::NamedEntity::name ) { this->lineEdit_name ->setText (this->obs->name()) ; if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::MashStep::type ) { this->comboBox_type ->setCurrentIndex(static_cast(obs->type())) ; if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::MashStep::infuseAmount_l ) { this->lineEdit_infuseAmount ->setAmount (this->obs->infuseAmount_l ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::MashStep::infuseTemp_c ) { this->lineEdit_infuseTemp ->setAmount (this->obs->infuseTemp_c ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::MashStep::decoctionAmount_l) { this->lineEdit_decoctionAmount->setAmount (this->obs->decoctionAmount_l()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::MashStep::stepTemp_c ) { this->lineEdit_stepTemp ->setAmount (this->obs->stepTemp_c ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::MashStep::stepTime_min ) { this->lineEdit_stepTime ->setAmount (this->obs->stepTime_min ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::MashStep::rampTime_min ) { this->lineEdit_rampTime ->setAmount (this->obs->rampTime_min ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::MashStep::endTemp_c ) { this->lineEdit_endTemp ->setAmount (this->obs->endTemp_c ()); if (!updateAll) { return; } } return; } void MashStepEditor::clear() { - lineEdit_name->setText(QString("")); - comboBox_type->setCurrentIndex(0); - lineEdit_infuseAmount->setText(QString("")); - lineEdit_infuseTemp->setText(QString("")); - lineEdit_decoctionAmount->setText(QString("")); - lineEdit_stepTemp->setText(QString("")); - lineEdit_stepTime->setText(QString("")); - lineEdit_rampTime->setText(QString("")); - lineEdit_endTemp->setText(QString("")); + this->lineEdit_name ->setText(QString("")); + this->comboBox_type ->setCurrentIndex(0); + this->lineEdit_infuseAmount ->setText(QString("")); + this->lineEdit_infuseTemp ->setText(QString("")); + this->lineEdit_decoctionAmount->setText(QString("")); + this->lineEdit_stepTemp ->setText(QString("")); + this->lineEdit_stepTime ->setText(QString("")); + this->lineEdit_rampTime ->setText(QString("")); + this->lineEdit_endTemp ->setText(QString("")); return; } @@ -124,15 +112,15 @@ void MashStepEditor::setMashStep(std::shared_ptr step) { } void MashStepEditor::saveAndClose() { - obs->setName(lineEdit_name->text()); - obs->setType(static_cast(comboBox_type->currentIndex())); - obs->setInfuseAmount_l(lineEdit_infuseAmount->toCanonical().quantity()); - obs->setInfuseTemp_c(lineEdit_infuseTemp->toCanonical().quantity()); - obs->setDecoctionAmount_l(lineEdit_decoctionAmount->toCanonical().quantity()); - obs->setStepTemp_c(lineEdit_stepTemp->toCanonical().quantity()); - obs->setStepTime_min(lineEdit_stepTime->toCanonical().quantity()); - obs->setRampTime_min(lineEdit_rampTime->toCanonical().quantity()); - obs->setEndTemp_c(lineEdit_endTemp->toCanonical().quantity()); + this->obs->setName (lineEdit_name->text()); + this->obs->setType (static_cast(comboBox_type->currentIndex())); + this->obs->setInfuseAmount_l (lineEdit_infuseAmount ->toCanonical().quantity()); + this->obs->setInfuseTemp_c (lineEdit_infuseTemp ->toCanonical().quantity()); + this->obs->setDecoctionAmount_l(lineEdit_decoctionAmount->toCanonical().quantity()); + this->obs->setStepTemp_c (lineEdit_stepTemp ->toCanonical().quantity()); + this->obs->setStepTime_min (lineEdit_stepTime ->toCanonical().quantity()); + this->obs->setRampTime_min (lineEdit_rampTime ->toCanonical().quantity()); + this->obs->setEndTemp_c (lineEdit_endTemp ->toCanonical().quantity()); if (this->obs->key() < 0) { // This is a new MashStep, so we need to store it. diff --git a/src/MiscDialog.cpp b/src/MiscDialog.cpp index 25e4fdd9..02fe4b51 100644 --- a/src/MiscDialog.cpp +++ b/src/MiscDialog.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * MiscDialog.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * MiscDialog.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Daniel Pettersson * • Matt Young @@ -38,8 +38,7 @@ MiscDialog::MiscDialog(MainWindow* parent) : QDialog(parent), mainWindow(parent), numMiscs(0), - miscEdit(new MiscEditor(this)) -{ + miscEdit(new MiscEditor(this)) { doLayout(); miscTableModel = new MiscTableModel(tableWidget, false); @@ -48,22 +47,24 @@ MiscDialog::MiscDialog(MainWindow* parent) : miscTableProxy->setSourceModel(miscTableModel); tableWidget->setModel(miscTableProxy); tableWidget->setSortingEnabled(true); - tableWidget->sortByColumn( MISCNAMECOL, Qt::AscendingOrder ); + tableWidget->sortByColumn(static_cast(MiscTableModel::ColumnIndex::Name), Qt::AscendingOrder); miscTableProxy->setDynamicSortFilter(true); miscTableProxy->setFilterKeyColumn(1); - connect( pushButton_addToRecipe, SIGNAL( clicked() ), this, SLOT( addMisc() ) ); - connect( pushButton_new, SIGNAL(clicked()), this, SLOT( newMisc() ) ); - connect( pushButton_edit, &QAbstractButton::clicked, this, &MiscDialog::editSelected ); - connect( pushButton_remove, &QAbstractButton::clicked, this, &MiscDialog::removeMisc ); - connect( tableWidget, &QAbstractItemView::doubleClicked, this, &MiscDialog::addMisc ); - connect( qLineEdit_searchBox, &QLineEdit::textEdited, this, &MiscDialog::filterMisc); + // Note, per https://wiki.qt.io/New_Signal_Slot_Syntax#Default_arguments_in_slot, the use of a trivial lambda + // function to allow use of default argument on newHop() slot +/// connect(this->pushButton_addToRecipe, &QAbstractButton::clicked, this, &MiscDialog::addMisc ); .:TODO:. Work out what this is supposed to do! + connect(this->pushButton_new, &QAbstractButton::clicked, this, [this]() { this->newMisc(); return; } ); + connect(this->pushButton_edit, &QAbstractButton::clicked, this, &MiscDialog::editSelected); + connect(this->pushButton_remove, &QAbstractButton::clicked, this, &MiscDialog::removeMisc ); + connect(this->tableWidget, &QAbstractItemView::doubleClicked, this, &MiscDialog::addMisc ); + connect(this->qLineEdit_searchBox, &QLineEdit::textEdited, this, &MiscDialog::filterMisc ); miscTableModel->observeDatabase(true); + return; } -void MiscDialog::doLayout() -{ +void MiscDialog::doLayout() { resize(800, 300); verticalLayout = new QVBoxLayout(this); tableWidget = new QTableView(this); @@ -102,10 +103,10 @@ void MiscDialog::doLayout() retranslateUi(); QMetaObject::connectSlotsByName(this); + return; } -void MiscDialog::retranslateUi() -{ +void MiscDialog::retranslateUi() { setWindowTitle(tr("Misc Database")); pushButton_addToRecipe->setText(tr("Add to Recipe")); pushButton_new->setText(tr("New")); @@ -117,23 +118,23 @@ void MiscDialog::retranslateUi() pushButton_edit->setToolTip(tr("Edit selected ingredient")); pushButton_remove->setToolTip(tr("Remove selected ingredient")); #endif // QT_NO_TOOLTIP + return; } -void MiscDialog::removeMisc() -{ +void MiscDialog::removeMisc() { QModelIndexList selected = tableWidget->selectionModel()->selectedIndexes(); - int row, size, i; - size = selected.size(); - if( size == 0 ) + int size = selected.size(); + if( size == 0 ) { return; + } // Make sure only one row is selected. - row = selected[0].row(); - for( i = 1; i < size; ++i ) - { - if( selected[i].row() != row ) + int row = selected[0].row(); + for (int i = 1; i < size; ++i ) { + if( selected[i].row() != row ) { return; + } } auto m = miscTableModel->getRow(miscTableProxy->mapToSource(selected[0]).row()); @@ -141,39 +142,37 @@ void MiscDialog::removeMisc() return; } -void MiscDialog::addMisc(const QModelIndex& index) -{ +void MiscDialog::addMisc(const QModelIndex& index) { QModelIndex translated; - if( !index.isValid() ) - { + if ( !index.isValid() ) { QModelIndexList selected = tableWidget->selectionModel()->selectedIndexes(); int row, size, i; size = selected.size(); - if( size == 0 ) + if( size == 0 ) { return; + } // Make sure only one row is selected. row = selected[0].row(); - for( i = 1; i < size; ++i ) - { - if( selected[i].row() != row ) + for( i = 1; i < size; ++i ) { + if( selected[i].row() != row ) { return; + } } // Always need to translate indices through the proxy translated = miscTableProxy->mapToSource(selected[0]); - } - else - { + } else { // Only respond if the name is selected. Since we connect to double-click signal, // this keeps us from adding something to the recipe when we just want to edit // one of the other columns. - if( index.column() == MISCNAMECOL ) + if (static_cast(index.column()) == MiscTableModel::ColumnIndex::Name) { translated = miscTableProxy->mapToSource(index); - else + } else { return; + } } MainWindow::instance().addMiscToRecipe(miscTableModel->getRow(translated.row())); @@ -204,13 +203,7 @@ void MiscDialog::editSelected() return; } -void MiscDialog::newMisc() -{ - newMisc(QString()); -} - -void MiscDialog::newMisc(QString folder) -{ +void MiscDialog::newMisc(QString folder) { QString name = QInputDialog::getText(this, tr("Misc name"), tr("Misc name:")); if(name.isEmpty()) @@ -222,10 +215,19 @@ void MiscDialog::newMisc(QString folder) miscEdit->setMisc(m); miscEdit->show(); + return; } -void MiscDialog::filterMisc(QString searchExpression) -{ +void MiscDialog::filterMisc(QString searchExpression) { miscTableProxy->setFilterCaseSensitivity(Qt::CaseInsensitive); miscTableProxy->setFilterFixedString(searchExpression); + return; +} + +void MiscDialog::changeEvent(QEvent* event) { + if(event->type() == QEvent::LanguageChange) { + retranslateUi(); + } + QDialog::changeEvent(event); + return; } diff --git a/src/MiscDialog.h b/src/MiscDialog.h index 4c16111d..0ef72fb1 100644 --- a/src/MiscDialog.h +++ b/src/MiscDialog.h @@ -1,7 +1,8 @@ /*====================================================================================================================== - * MiscDialog.h is part of Brewken, and is copyright the following authors 2009-2015: + * MiscDialog.h is part of Brewken, and is copyright the following authors 2009-2023: * • Daniel Pettersson * • Jeff Bailey + * • Matt Young * • Mik Firestone * • Philip Greggory Lee * @@ -60,7 +61,6 @@ class MiscDialog : public QDialog { QPushButton *pushButton_remove; //! @} - void newMisc(QString folder); public slots: //! Add the selected misc to the current recipe. void addMisc(const QModelIndex& = QModelIndex()); @@ -69,18 +69,13 @@ public slots: //! Bring up the editor for the selected misc. void editSelected(); //! Add a new misc to the database. - void newMisc(); + void newMisc(QString folder = ""); //! Filter out the matching miscs. void filterMisc(QString searchExpression); protected: - virtual void changeEvent(QEvent* event) - { - if(event->type() == QEvent::LanguageChange) - retranslateUi(); - QDialog::changeEvent(event); - } + virtual void changeEvent(QEvent* event); private: MainWindow* mainWindow; diff --git a/src/MiscEditor.cpp b/src/MiscEditor.cpp index a44a8078..6f10d633 100644 --- a/src/MiscEditor.cpp +++ b/src/MiscEditor.cpp @@ -36,9 +36,9 @@ MiscEditor::MiscEditor(QWidget * parent) : tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs); - SMART_LINE_EDIT_INIT(MiscEditor, Misc, lineEdit_name , PropertyNames::NamedEntity::name ); - SMART_LINE_EDIT_INIT(MiscEditor, Misc, lineEdit_inventory, PropertyNames::Misc::amount , *this->label_inventory); - SMART_LINE_EDIT_INIT(MiscEditor, Misc, lineEdit_time , PropertyNames::Misc::time , *this->label_time ); + SMART_FIELD_INIT(MiscEditor, label_name , lineEdit_name , Misc, PropertyNames::NamedEntity::name); + SMART_FIELD_INIT(MiscEditor, label_inventory, lineEdit_inventory, Misc, PropertyNames::Misc::amount ); + SMART_FIELD_INIT(MiscEditor, label_time , lineEdit_time , Misc, PropertyNames::Misc::time ); // Note, per https://wiki.qt.io/New_Signal_Slot_Syntax#Default_arguments_in_slot, the use of a trivial lambda // function to allow use of default argument on newStyle() slot @@ -157,7 +157,7 @@ void MiscEditor::newMisc(QString folder) { void MiscEditor::setIsWeight(bool const state) { qDebug() << Q_FUNC_INFO << "state is" << state; // But you have to admit, this is clever - this->lineEdit_inventory->getUiAmountWithUnits().selectPhysicalQuantity( + this->lineEdit_inventory->selectPhysicalQuantity( state ? Measurement::PhysicalQuantity::Mass : Measurement::PhysicalQuantity::Volume ); diff --git a/src/MiscSortFilterProxyModel.cpp b/src/MiscSortFilterProxyModel.cpp index 97385e91..d18994ea 100644 --- a/src/MiscSortFilterProxyModel.cpp +++ b/src/MiscSortFilterProxyModel.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * MiscSortFilterProxyModel.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * MiscSortFilterProxyModel.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Daniel Pettersson * • Matt Young * • Mik Firestone @@ -42,8 +42,9 @@ bool MiscSortFilterProxyModel::lessThan(const QModelIndex &left, rightMisc = source->data(right); } - switch (left.column()) { - case MISCINVENTORYCOL: + auto const columnIndex = static_cast(left.column()); + switch (columnIndex) { + case MiscTableModel::ColumnIndex::Inventory: if (Measurement::qStringToSI(leftMisc.toString(), Measurement::PhysicalQuantity::Mass).quantity() == 0.0 && this->sortOrder() == Qt::AscendingOrder) { return false; @@ -51,11 +52,11 @@ bool MiscSortFilterProxyModel::lessThan(const QModelIndex &left, return (Measurement::qStringToSI(leftMisc.toString(), Measurement::PhysicalQuantity::Mass) < Measurement::qStringToSI(rightMisc.toString(), Measurement::PhysicalQuantity::Mass)); - case MISCAMOUNTCOL: + case MiscTableModel::ColumnIndex::Amount: return (Measurement::qStringToSI(leftMisc.toString(), Measurement::PhysicalQuantity::Mass) < Measurement::qStringToSI(rightMisc.toString(), Measurement::PhysicalQuantity::Mass)); - case MISCTIMECOL: + case MiscTableModel::ColumnIndex::Time: return (Measurement::qStringToSI(leftMisc.toString(), Measurement::PhysicalQuantity::Time) < Measurement::qStringToSI(rightMisc.toString(), Measurement::PhysicalQuantity::Time)); diff --git a/src/NamedMashEditor.cpp b/src/NamedMashEditor.cpp index 15832ee3..91d1307b 100644 --- a/src/NamedMashEditor.cpp +++ b/src/NamedMashEditor.cpp @@ -59,18 +59,27 @@ NamedMashEditor::NamedMashEditor(QWidget* parent, MashStepEditor* editor, bool s //! And do some fun stuff with the equipment this->equipListModel = new EquipmentListModel(equipmentComboBox); this->equipmentComboBox->setModel(equipListModel); - connect(equipmentComboBox, &QComboBox::currentTextChanged, this, &NamedMashEditor::fromEquipment ); + + SMART_FIELD_INIT(NamedMashEditor, label_name , lineEdit_name , Mash, PropertyNames::NamedEntity::name ); + SMART_FIELD_INIT(NamedMashEditor, label_grainTemp , lineEdit_grainTemp , Mash, PropertyNames::Mash::grainTemp_c , 1); + SMART_FIELD_INIT(NamedMashEditor, label_spargeTemp, lineEdit_spargeTemp, Mash, PropertyNames::Mash::spargeTemp_c , 1); + SMART_FIELD_INIT(NamedMashEditor, label_spargePh , lineEdit_spargePh , Mash, PropertyNames::Mash::ph , 0); + SMART_FIELD_INIT(NamedMashEditor, label_tunTemp , lineEdit_tunTemp , Mash, PropertyNames::Mash::tunTemp_c , 1); + SMART_FIELD_INIT(NamedMashEditor, label_tunMass , lineEdit_tunMass , Mash, PropertyNames::Mash::tunWeight_kg ); + SMART_FIELD_INIT(NamedMashEditor, label_tunSpHeat , lineEdit_tunSpHeat , Mash, PropertyNames::Mash::tunSpecificHeat_calGC, 1); + + connect(this->equipmentComboBox, &QComboBox::currentTextChanged, this, &NamedMashEditor::fromEquipment ); // ok and cancel buttons - connect(pushButton_save, &QAbstractButton::clicked, this, &NamedMashEditor::saveAndClose ); - connect(pushButton_cancel, &QAbstractButton::clicked, this, &NamedMashEditor::closeEditor ); + connect(this->pushButton_save, &QAbstractButton::clicked, this, &NamedMashEditor::saveAndClose ); + connect(this->pushButton_cancel, &QAbstractButton::clicked, this, &NamedMashEditor::closeEditor ); // new mash step, delete mash step, move mash step up and down - connect(pushButton_addMashStep, &QAbstractButton::clicked, this, &NamedMashEditor::addMashStep ); - connect(pushButton_removeMashStep, &QAbstractButton::clicked, this, &NamedMashEditor::removeMashStep ); - connect(pushButton_mashUp, &QAbstractButton::clicked, this, &NamedMashEditor::moveMashStepUp ); - connect(pushButton_mashDown, &QAbstractButton::clicked, this, &NamedMashEditor::moveMashStepDown); + connect(this->pushButton_addMashStep, &QAbstractButton::clicked, this, &NamedMashEditor::addMashStep ); + connect(this->pushButton_removeMashStep, &QAbstractButton::clicked, this, &NamedMashEditor::removeMashStep ); + connect(this->pushButton_mashUp, &QAbstractButton::clicked, this, &NamedMashEditor::moveMashStepUp ); + connect(this->pushButton_mashDown, &QAbstractButton::clicked, this, &NamedMashEditor::moveMashStepDown); // finally, the combo box and the remove mash button - connect(mashComboBox, &QComboBox::currentTextChanged, this, &NamedMashEditor::mashSelected ); - connect(pushButton_remove, &QAbstractButton::clicked, this, &NamedMashEditor::removeMash ); + connect(this->mashComboBox, &QComboBox::currentTextChanged, this, &NamedMashEditor::mashSelected ); + connect(this->pushButton_remove, &QAbstractButton::clicked, this, &NamedMashEditor::removeMash ); this->setMash(mashListModel->at(mashComboBox->currentIndex())); return; @@ -148,12 +157,12 @@ void NamedMashEditor::showChanges(QMetaProperty* prop) { qDebug() << Q_FUNC_INFO << "Updating" << (updateAll ? "all" : "property") << propName; if (updateAll || propName == PropertyNames::NamedEntity::name ) {lineEdit_name ->setText (mashObs->name ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Mash::grainTemp_c ) {lineEdit_grainTemp ->setText (mashObs->grainTemp_c ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Mash::spargeTemp_c ) {lineEdit_spargeTemp->setText (mashObs->spargeTemp_c ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Mash::ph ) {lineEdit_spargePh ->setText (mashObs->ph ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Mash::tunTemp_c ) {lineEdit_tunTemp ->setText (mashObs->tunTemp_c ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Mash::tunWeight_kg ) {lineEdit_tunMass ->setText (mashObs->tunWeight_kg ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Mash::tunSpecificHeat_calGC) {lineEdit_tunSpHeat ->setText (mashObs->tunSpecificHeat_calGC()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Mash::grainTemp_c ) {lineEdit_grainTemp ->setAmount (mashObs->grainTemp_c ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Mash::spargeTemp_c ) {lineEdit_spargeTemp->setAmount (mashObs->spargeTemp_c ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Mash::ph ) {lineEdit_spargePh ->setAmount (mashObs->ph ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Mash::tunTemp_c ) {lineEdit_tunTemp ->setAmount (mashObs->tunTemp_c ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Mash::tunWeight_kg ) {lineEdit_tunMass ->setAmount (mashObs->tunWeight_kg ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Mash::tunSpecificHeat_calGC) {lineEdit_tunSpHeat ->setAmount (mashObs->tunSpecificHeat_calGC()); if (!updateAll) { return; } } if (updateAll || propName == PropertyNames::Mash::notes ) {textEdit_notes ->setPlainText(mashObs->notes ()); if (!updateAll) { return; } } } @@ -258,8 +267,8 @@ void NamedMashEditor::fromEquipment([[maybe_unused]] QString const & name) { } Equipment * selected = equipListModel->at(equipmentComboBox->currentIndex()); if (selected) { - lineEdit_tunMass ->setText(selected->tunWeight_kg ()); - lineEdit_tunSpHeat->setText(selected->tunSpecificHeat_calGC()); + lineEdit_tunMass ->setAmount(selected->tunWeight_kg ()); + lineEdit_tunSpHeat->setAmount(selected->tunSpecificHeat_calGC()); } return; } diff --git a/src/OgAdjuster.cpp b/src/OgAdjuster.cpp index 0b187df4..adf21455 100644 --- a/src/OgAdjuster.cpp +++ b/src/OgAdjuster.cpp @@ -26,12 +26,21 @@ #include "model/Equipment.h" #include "model/Recipe.h" -OgAdjuster::OgAdjuster( QWidget* parent ) : QDialog(parent) { +OgAdjuster::OgAdjuster( QWidget* parent ) : + QDialog{parent}, + recObs {nullptr} { setupUi(this); - recObs = 0; + SMART_FIELD_INIT_FIXED(OgAdjuster, label_sg , lineEdit_sg , double, Measurement::Units::sp_grav , 3); // Input: SG + SMART_FIELD_INIT_FS (OgAdjuster, label_temp , lineEdit_temp , double, Measurement::PhysicalQuantity::Temperature, 1); // Input: Temp + SMART_FIELD_INIT_FS (OgAdjuster, label_calTemp , lineEdit_calTemp , double, Measurement::PhysicalQuantity::Temperature, 1); // Input: Calibration Temp + SMART_FIELD_INIT_FIXED(OgAdjuster, label_plato , lineEdit_plato , double, Measurement::Units::plato , 1); // Input: Plato + SMART_FIELD_INIT_FS (OgAdjuster, label_volume , lineEdit_volume , double, Measurement::PhysicalQuantity::Volume ); // Input: Pre-Boil Volume + SMART_FIELD_INIT_FIXED(OgAdjuster, label_og , lineEdit_og , double, Measurement::Units::sp_grav , 3); // Output: OG w/o Correction + SMART_FIELD_INIT_FS (OgAdjuster, label_add , lineEdit_add , double, Measurement::PhysicalQuantity::Volume ); // Output: Add to Boil + SMART_FIELD_INIT_FS (OgAdjuster, label_batchSize, lineEdit_batchSize, double, Measurement::PhysicalQuantity::Volume ); // Output: Final Batch Size - connect( pushButton_calculate, &QAbstractButton::clicked, this, &OgAdjuster::calculate ); + connect(this->pushButton_calculate, &QAbstractButton::clicked, this, &OgAdjuster::calculate ); return; } @@ -48,7 +57,7 @@ void OgAdjuster::calculate() { // Get inputs. double sg = lineEdit_sg->toCanonical().quantity(); - bool okPlato = true; + bool okPlato = true; double plato = Measurement::extractRawFromString(lineEdit_plato->text(), &okPlato); double temp_c = lineEdit_temp->toCanonical().quantity(); double hydroTemp_c = lineEdit_calTemp->toCanonical().quantity(); @@ -74,17 +83,14 @@ void OgAdjuster::calculate() { // Calculate missing input parameters. double sg_20C = 0.0; - if( gotSG ) - { + if (gotSG) { double sg_15C = sg * Algorithms::getWaterDensity_kgL(hydroTemp_c)/Algorithms::getWaterDensity_kgL(15) + Algorithms::hydrometer15CCorrection( temp_c ); sg_20C = sg_15C * Algorithms::getWaterDensity_kgL(15)/Algorithms::getWaterDensity_kgL(20); - plato = Algorithms::SG_20C20C_toPlato( sg_20C ); - lineEdit_plato->setText( sg_20C ); //Event if the display is in Plato, we must send it in default unit - } - else - { - sg_20C = Algorithms::PlatoToSG_20C20C( plato ); + plato = Algorithms::SG_20C20C_toPlato(sg_20C); + lineEdit_plato->setAmount(sg_20C); // Event if the display is in Plato, we must send it in default unit + } else { + sg_20C = Algorithms::PlatoToSG_20C20C(plato); } // Calculate intermediate parameters. @@ -116,8 +122,8 @@ void OgAdjuster::calculate() { finalVolume_l += waterToAdd_l; // Display output. - lineEdit_og->setText(finalUncorrectedSg_20C); - lineEdit_add->setText(waterToAdd_l); - lineEdit_batchSize->setText(finalVolume_l); + this->lineEdit_og ->setAmount(finalUncorrectedSg_20C); + this->lineEdit_add ->setAmount(waterToAdd_l); + this->lineEdit_batchSize->setAmount(finalVolume_l); return; } diff --git a/src/OptionDialog.cpp b/src/OptionDialog.cpp index 945cdcf9..c7bcd309 100644 --- a/src/OptionDialog.cpp +++ b/src/OptionDialog.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * OptionDialog.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * OptionDialog.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Daniel Pettersson * • Greg Meess @@ -40,7 +40,6 @@ #include #include -#include "BtLineEdit.h" #include "database/Database.h" #include "Localization.h" #include "Logging.h" diff --git a/src/PitchDialog.cpp b/src/PitchDialog.cpp index 1b1d1f32..7b1940ce 100644 --- a/src/PitchDialog.cpp +++ b/src/PitchDialog.cpp @@ -33,24 +33,30 @@ PitchDialog::PitchDialog(QWidget* parent) : QDialog(parent) { setupUi(this); // Set default dates - dateEdit_ProductionDate->setMaximumDate(QDate::currentDate()); - dateEdit_ProductionDate->setDate(QDate::currentDate()); - updateViabilityFromDate(QDate::currentDate()); - - connect(lineEdit_vol, &BtLineEdit::textModified, this, &PitchDialog::calculate); - connect(lineEdit_OG, &BtLineEdit::textModified, this, &PitchDialog::calculate); - connect(slider_pitchRate, &QAbstractSlider::valueChanged, this, &PitchDialog::calculate); - connect(slider_pitchRate, &QAbstractSlider::valueChanged, this, &PitchDialog::updateShownPitchRate); - connect(spinBox_Viability, QOverload::of(&QSpinBox::valueChanged), this, &PitchDialog::calculate); - connect(spinBox_VialsPitched, QOverload::of(&QSpinBox::valueChanged), this, &PitchDialog::calculate); - connect(comboBox_AerationMethod, QOverload::of(&QComboBox::currentIndexChanged), this, &PitchDialog::calculate); - connect(dateEdit_ProductionDate, &QDateTimeEdit::dateChanged, this, &PitchDialog::updateViabilityFromDate); - connect(checkBox_CalculateViability, &QCheckBox::stateChanged, this, &PitchDialog::toggleViabilityFromDate); + this->dateEdit_ProductionDate->setMaximumDate(QDate::currentDate()); + this->dateEdit_ProductionDate->setDate(QDate::currentDate()); + this->updateViabilityFromDate(QDate::currentDate()); + + SMART_FIELD_INIT_FS(PitchDialog, label_vol , lineEdit_vol , double, Measurement::PhysicalQuantity::Volume ); // Input: Wort Volume + SMART_FIELD_INIT_FS(PitchDialog, label_og , lineEdit_OG , double, Measurement::PhysicalQuantity::Density ); // Input: OG + SMART_FIELD_INIT_FS(PitchDialog, label_cells , lineEdit_cells , double, NonPhysicalQuantity::Dimensionless, 1); // Output: Billions of Yeast Cells Required + SMART_FIELD_INIT_FS(PitchDialog, label_vials , lineEdit_vials , double, NonPhysicalQuantity::Dimensionless, 0); // Output: # Vials/Smack Packs w/o Starter + SMART_FIELD_INIT_FS(PitchDialog, label_yeast , lineEdit_yeast , double, Measurement::PhysicalQuantity::Mass ); // Output: Dry Yeast + SMART_FIELD_INIT_FS(PitchDialog, label_starterVol, lineEdit_starterVol, double, Measurement::PhysicalQuantity::Volume ); // Output: Starter Volume + + connect(this->lineEdit_vol, &SmartLineEdit::textModified, this, &PitchDialog::calculate ); + connect(this->lineEdit_OG, &SmartLineEdit::textModified, this, &PitchDialog::calculate ); + connect(this->slider_pitchRate, &QAbstractSlider::valueChanged, this, &PitchDialog::calculate ); + connect(this->slider_pitchRate, &QAbstractSlider::valueChanged, this, &PitchDialog::updateShownPitchRate ); + connect(this->spinBox_Viability, QOverload::of(&QSpinBox::valueChanged), this, &PitchDialog::calculate ); + connect(this->spinBox_VialsPitched, QOverload::of(&QSpinBox::valueChanged), this, &PitchDialog::calculate ); + connect(this->comboBox_AerationMethod, QOverload::of(&QComboBox::currentIndexChanged), this, &PitchDialog::calculate ); + connect(this->dateEdit_ProductionDate, &QDateTimeEdit::dateChanged, this, &PitchDialog::updateViabilityFromDate); + connect(this->checkBox_CalculateViability, &QCheckBox::stateChanged, this, &PitchDialog::toggleViabilityFromDate); // Dates are a little more cranky -/// connect(label_productionDate, &BtLabel::changedSystemOfMeasurementOrScale, this, &PitchDialog::updateProductionDate); this->updateProductionDate(); - updateShownPitchRate(0); + this->updateShownPitchRate(0); return; } @@ -69,11 +75,11 @@ void PitchDialog::updateProductionDate() { } void PitchDialog::setWortVolume_l(double volume) { - lineEdit_vol->setText(volume); + this->lineEdit_vol->setAmount(volume); } void PitchDialog::setWortDensity(double sg) { - lineEdit_OG->setText(sg); + this->lineEdit_OG->setAmount(sg); } void PitchDialog::calculate() { @@ -94,7 +100,7 @@ void PitchDialog::calculate() { double dry_g = cells / 20e9; // 20 billion cells per dry gram. // Set the maximum number of vials pitched based on # of vials needed without a starter. - spinBox_VialsPitched->setMaximum(vials < 1 ? 1 : floor(vials)); + this->spinBox_VialsPitched->setMaximum(vials < 1 ? 1 : floor(vials)); // Set the aeration factor for the starter size double aerationFactor; @@ -117,10 +123,10 @@ void PitchDialog::calculate() { double inoculationRate = pow((12.522 / growthRate), 2.18); double starterVol_l = totalCellsPitched / (inoculationRate * aerationFactor); - lineEdit_cells->setText(cells/1e9, 1); - lineEdit_starterVol->setText(starterVol_l); - lineEdit_yeast->setText(dry_g/1000); //Needs to be converted into default unit (kg) - lineEdit_vials->setText(vials,0); + this->lineEdit_cells ->setAmount(cells/1e9); + this->lineEdit_starterVol->setAmount(starterVol_l); + this->lineEdit_yeast ->setAmount(dry_g/1000); //Needs to be converted into default unit (kg) + this->lineEdit_vials ->setAmount(vials); return; } @@ -129,7 +135,7 @@ void PitchDialog::updateShownPitchRate(int percent) { double rate_MpermLP = (2-0.75) * ((double)percent) / 100.0 + 0.75; // NOTE: We are changing the LABEL here, not the LineEdit. Leave it be - label_pitchRate->setText( QString("%L1").arg(rate_MpermLP, 1, 'f', 2, QChar('0')) ); + this->label_pitchRate->setText( QString("%L1").arg(rate_MpermLP, 1, 'f', 2, QChar('0')) ); return; } @@ -141,14 +147,14 @@ void PitchDialog::toggleViabilityFromDate(int state) { if (state == Qt::Unchecked) { // If the box is not checked, disable the date and allow // the user to manually set the viability. - spinBox_Viability->setEnabled(true); - dateEdit_ProductionDate->setEnabled(false); + this->spinBox_Viability->setEnabled(true); + this->dateEdit_ProductionDate->setEnabled(false); } else if (state == Qt::Checked) { // If the box is checked, prevent the user from manually setting // the viability. Use the date editor instead. - spinBox_Viability->setEnabled(false); - dateEdit_ProductionDate->setEnabled(true); - updateViabilityFromDate(dateEdit_ProductionDate->date()); + this->spinBox_Viability->setEnabled(false); + this->dateEdit_ProductionDate->setEnabled(true); + this->updateViabilityFromDate(dateEdit_ProductionDate->date()); } return; } @@ -160,6 +166,6 @@ void PitchDialog::updateViabilityFromDate(QDate date) { // Set the viability based on the number of days since the yeast // production date. int daysDifference = date.daysTo(QDate::currentDate()); - spinBox_Viability->setValue(97 - 0.7 * daysDifference); + this->spinBox_Viability->setValue(97 - 0.7 * daysDifference); return; } diff --git a/src/PrimingDialog.cpp b/src/PrimingDialog.cpp index ca0b65d4..12d13244 100644 --- a/src/PrimingDialog.cpp +++ b/src/PrimingDialog.cpp @@ -38,10 +38,10 @@ PrimingDialog::PrimingDialog(QWidget* parent) : QDialog(parent) { this->sugarGroup->addButton(radioButton_sucrose); this->sugarGroup->addButton(radioButton_dme); - SMART_LINE_EDIT_INIT_FS(PrimingDialog, lineEdit_beerVol, double, Measurement::PhysicalQuantity::Volume , *this->label_beerVol ); - SMART_LINE_EDIT_INIT_FS(PrimingDialog, lineEdit_temp , double, Measurement::PhysicalQuantity::Temperature, *this->label_temp , 1); - SMART_LINE_EDIT_INIT_FS(PrimingDialog, lineEdit_vols , double, Measurement::PhysicalQuantity::Carbonation, *this->label_vols , 1); - SMART_LINE_EDIT_INIT_FS(PrimingDialog, lineEdit_output , double, Measurement::PhysicalQuantity::Mass , *this->label_output ); + SMART_FIELD_INIT_FS(PrimingDialog, label_beerVol, lineEdit_beerVol, double, Measurement::PhysicalQuantity::Volume ); + SMART_FIELD_INIT_FS(PrimingDialog, label_temp , lineEdit_temp , double, Measurement::PhysicalQuantity::Temperature, 1); + SMART_FIELD_INIT_FS(PrimingDialog, label_vols , lineEdit_vols , double, Measurement::PhysicalQuantity::Carbonation, 1); + SMART_FIELD_INIT_FS(PrimingDialog, label_output , lineEdit_output , double, Measurement::PhysicalQuantity::Mass ); connect(pushButton_calculate, &QAbstractButton::clicked, this, &PrimingDialog::calculate); return; @@ -88,7 +88,7 @@ void PrimingDialog::calculate() { return; } - // The amount have to be set in default unit to BtLineEdit. + // The amount have to be set in default unit to SmartLineEdit. // We should find a better solution, but until it is not, we must do it this way. lineEdit_output->setAmount(sugar_g/1000); diff --git a/src/RadarChart.cpp b/src/RadarChart.cpp index f7bb594b..c1e1f5d5 100644 --- a/src/RadarChart.cpp +++ b/src/RadarChart.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * RadarChart.cpp is part of Brewken, and is copyright the following authors 2021-2022: + * RadarChart.cpp is part of Brewken, and is copyright the following authors 2021-2023: * • Matt Young * * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -16,12 +16,8 @@ #include "RadarChart.h" #include -// We need an extra define on Windows to access the M_PI constant in cmath. (More details in comment below.) -#ifdef Q_OS_WIN -#define _USE_MATH_DEFINES -#endif #include -//#include Uncomment this when we switch to C++20 +#include // For std::numbers::pi #include #include @@ -39,12 +35,7 @@ namespace { // // For functions, angles are all in radians, and the "natural" coordinate system measures anti-clockwise // starting from 3 o'clock as 0rad. There are 2π radians in a circle. - // - // When we switch to C++20, we should replace M_PI (non-standard but usually defined in cmath) with std::numbers::pi - // (standard as of C++20). Note that, on Windows, - // per https://docs.microsoft.com/en-us/cpp/c-runtime-library/math-constants?view=msvc-160, we also have to - // #define _USE_MATH_DEFINES to use M_PI. - double const RadiansInACircle = 2 * M_PI; + double const RadiansInACircle = 2 * std::numbers::pi; double const StartingAngleInRadians = RadiansInACircle / 4; diff --git a/src/RecipeExtrasWidget.cpp b/src/RecipeExtrasWidget.cpp index ccdedcf2..97fb936e 100644 --- a/src/RecipeExtrasWidget.cpp +++ b/src/RecipeExtrasWidget.cpp @@ -23,7 +23,6 @@ #include #include -#include "BtLabel.h" #include "MainWindow.h" #include "measurement/Unit.h" #include "model/Recipe.h" @@ -35,24 +34,26 @@ RecipeExtrasWidget::RecipeExtrasWidget(QWidget* parent) : this->setupUi(this); - SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_brewer , PropertyNames::Recipe::brewer ); - SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_asstBrewer , PropertyNames::Recipe::asstBrewer ); - SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_primaryAge , PropertyNames::Recipe::primaryAge_days , 0); - SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_primaryTemp, PropertyNames::Recipe::primaryTemp_c , *this->label_primaryTemp, 1); - SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_secAge , PropertyNames::Recipe::secondaryAge_days , 0); - SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_secTemp , PropertyNames::Recipe::secondaryTemp_c , *this->label_secTemp , 1); - SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_tertAge , PropertyNames::Recipe::tertiaryAge_days , 0); - SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_tertTemp , PropertyNames::Recipe::tertiaryTemp_c , *this->label_tertTemp , 1); - SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_age , PropertyNames::Recipe::age_days , 0); - SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_ageTemp , PropertyNames::Recipe::ageTemp_c , *this->label_ageTemp , 1); - SMART_LINE_EDIT_INIT(RecipeExtrasWidget, Recipe, lineEdit_carbVols , PropertyNames::Recipe::carbonation_vols , *this->label_carbVols ); + // Note that label_primaryAge, label_secAge, label_tertAge, label_age are QLabel, not SmartLabel, as we're "forcing" + // the measurement to be in days rather than allowing the usual units of PhysicalQuantity::Time + SMART_FIELD_INIT(RecipeExtrasWidget, label_brewer , lineEdit_brewer , Recipe, PropertyNames::Recipe::brewer ); + SMART_FIELD_INIT(RecipeExtrasWidget, label_asstBrewer , lineEdit_asstBrewer , Recipe, PropertyNames::Recipe::asstBrewer ); + SMART_FIELD_INIT(RecipeExtrasWidget, label_primaryAge , lineEdit_primaryAge , Recipe, PropertyNames::Recipe::primaryAge_days , 0); + SMART_FIELD_INIT(RecipeExtrasWidget, label_primaryTemp, lineEdit_primaryTemp, Recipe, PropertyNames::Recipe::primaryTemp_c , 1); + SMART_FIELD_INIT(RecipeExtrasWidget, label_secAge , lineEdit_secAge , Recipe, PropertyNames::Recipe::secondaryAge_days, 0); + SMART_FIELD_INIT(RecipeExtrasWidget, label_secTemp , lineEdit_secTemp , Recipe, PropertyNames::Recipe::secondaryTemp_c , 1); + SMART_FIELD_INIT(RecipeExtrasWidget, label_tertAge , lineEdit_tertAge , Recipe, PropertyNames::Recipe::tertiaryAge_days , 0); + SMART_FIELD_INIT(RecipeExtrasWidget, label_tertTemp , lineEdit_tertTemp , Recipe, PropertyNames::Recipe::tertiaryTemp_c , 1); + SMART_FIELD_INIT(RecipeExtrasWidget, label_age , lineEdit_age , Recipe, PropertyNames::Recipe::age_days , 0); + SMART_FIELD_INIT(RecipeExtrasWidget, label_ageTemp , lineEdit_ageTemp , Recipe, PropertyNames::Recipe::ageTemp_c , 1); + SMART_FIELD_INIT(RecipeExtrasWidget, label_carbVols , lineEdit_carbVols , Recipe, PropertyNames::Recipe::carbonation_vols ); // See comment in model/Recipe.cpp about things we measure in days. If we switched them from Dimensionless to Time, // we would need something like this - // this->lineEdit_primaryAge->getUiAmountWithUnits().setForcedRelativeScale(Measurement::UnitSystem::RelativeScale::Large); - // this->lineEdit_secAge ->getUiAmountWithUnits().setForcedRelativeScale(Measurement::UnitSystem::RelativeScale::Large); - // this->lineEdit_tertAge ->getUiAmountWithUnits().setForcedRelativeScale(Measurement::UnitSystem::RelativeScale::Large); - // this->lineEdit_age ->getUiAmountWithUnits().setForcedRelativeScale(Measurement::UnitSystem::RelativeScale::Large); + // this->lineEdit_primaryAge->getSmartField().setForcedRelativeScale(Measurement::UnitSystem::RelativeScale::Large); + // this->lineEdit_secAge ->getSmartField().setForcedRelativeScale(Measurement::UnitSystem::RelativeScale::Large); + // this->lineEdit_tertAge ->getSmartField().setForcedRelativeScale(Measurement::UnitSystem::RelativeScale::Large); + // this->lineEdit_age ->getSmartField().setForcedRelativeScale(Measurement::UnitSystem::RelativeScale::Large); connect(this->lineEdit_age , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateAge ); connect(this->lineEdit_ageTemp , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateAgeTemp ); diff --git a/src/RecipeFormatter.cpp b/src/RecipeFormatter.cpp index 2fae6f25..ae1ad734 100644 --- a/src/RecipeFormatter.cpp +++ b/src/RecipeFormatter.cpp @@ -294,16 +294,12 @@ class RecipeFormatter::impl { "%1" "%2") .arg(tr("Batch Size")) - .arg(Measurement::displayAmount(Measurement::Amount{rec->finalVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::finalVolume_l)); + .arg(Measurement::displayAmount(Measurement::Amount{rec->finalVolume_l(), Measurement::Units::liters})); body += QString("%1" "%2" "") .arg(tr("Boil Size")) - .arg(Measurement::displayAmount(Measurement::Amount{rec->boilVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilVolume_l)); + .arg(Measurement::displayAmount(Measurement::Amount{rec->boilVolume_l(), Measurement::Units::liters})); // Second row: Boil Time and Efficiency body += QString("" "%1" @@ -312,9 +308,7 @@ class RecipeFormatter::impl { .arg(Measurement::displayAmount(Measurement::Amount{ rec->equipment() == nullptr ? 0.0 : rec->equipment()->boilTime_min(), Measurement::Units::minutes - }, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilTime_min)); + })); body += QString("%1" "%2") .arg(tr("Efficiency")) @@ -325,17 +319,11 @@ class RecipeFormatter::impl { "%1" "%2") .arg(tr("OG")) - .arg(Measurement::displayAmount(Measurement::Amount{rec->og(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::og, - 3)); + .arg(Measurement::displayAmount(Measurement::Amount{rec->og(), Measurement::Units::sp_grav}, 3)); body += QString("%1" "%2") .arg(tr("FG")) - .arg(Measurement::displayAmount(Measurement::Amount{rec->fg(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::fg, - 3)); + .arg(Measurement::displayAmount(Measurement::Amount{rec->fg(), Measurement::Units::sp_grav}, 3)); // Fourth row: ABV and Bitterness. We need to set the bitterness string up first body += QString("" @@ -354,10 +342,7 @@ class RecipeFormatter::impl { "%1" "%2 (%3)") .arg(tr("Color")) - .arg(Measurement::displayAmount(Measurement::Amount{rec->color_srm(), Measurement::Units::srm}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::color_srm, - 1)) + .arg(Measurement::displayAmount(Measurement::Amount{rec->color_srm(), Measurement::Units::srm}, 1)) .arg(ColorMethods::colorFormulaName()); bool displayMetricVolumes = @@ -384,34 +369,26 @@ class RecipeFormatter::impl { QStringList entry, value; entry.append(tr("Batch Size")); - value.append(QString("%1").arg(Measurement::displayAmount(Measurement::Amount{rec->finalVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::finalVolume_l))); + value.append(QString("%1").arg(Measurement::displayAmount(Measurement::Amount{rec->finalVolume_l(), + Measurement::Units::liters}))); entry.append(tr("Boil Size")); - value.append(QString("%1").arg(Measurement::displayAmount(Measurement::Amount{rec->boilVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilVolume_l))); + value.append(QString("%1").arg(Measurement::displayAmount(Measurement::Amount{rec->boilVolume_l(), + Measurement::Units::liters}))); entry.append(tr("Boil Time")); value.append( QString("%1").arg( Measurement::displayAmount(Measurement::Amount{rec->equipment() == nullptr ? 0.0 : rec->equipment()->boilTime_min(), - Measurement::Units::minutes}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilTime_min) + Measurement::Units::minutes}) ) ); entry.append(tr("Efficiency")); value.append(QString("%1%").arg(rec->efficiency_pct(), 0, 'f', 0)); entry.append(tr("OG")); - value.append(QString("%1").arg(Measurement::displayAmount(Measurement::Amount{rec->og(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::og, - 3))); + value.append(QString("%1").arg(Measurement::displayAmount(Measurement::Amount{rec->og(), + Measurement::Units::sp_grav}, 3))); entry.append(tr("FG")); - value.append(QString("%1").arg(Measurement::displayAmount(Measurement::Amount{rec->fg(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::fg, - 3))); + value.append(QString("%1").arg(Measurement::displayAmount(Measurement::Amount{rec->fg(), + Measurement::Units::sp_grav}, 3))); entry.append(tr("ABV")); value.append(QString("%1%").arg(Measurement::displayQuantity(rec->ABV_pct(), 1))); entry.append(tr("Bitterness")); @@ -419,10 +396,8 @@ class RecipeFormatter::impl { .arg(tr("IBU")) .arg(IbuMethods::ibuFormulaName())); entry.append(tr("Color")); - value.append(QString("%1 (%2)").arg(Measurement::displayAmount(Measurement::Amount{rec->color_srm(), Measurement::Units::srm}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::color_srm, - 1)) + value.append(QString("%1 (%2)").arg(Measurement::displayAmount(Measurement::Amount{rec->color_srm(), + Measurement::Units::srm}, 1)) .arg(ColorMethods::colorFormulaName())); padAllToMaxLength(&entry); @@ -474,24 +449,17 @@ class RecipeFormatter::impl { ftable += QString("%1%2%3%4%5%6%%7") .arg(ferm->name()) .arg(Fermentable::typeDisplayNames[ferm->type()]) - .arg(Measurement::displayAmount(ferm->amountWithUnits(), - PersistentSettings::Sections::fermentableTable, - PropertyNames::Fermentable::amountWithUnits)) + .arg(Measurement::displayAmount(ferm->amountWithUnits())) .arg(ferm->isMashed() ? tr("Yes") : tr("No") ) .arg(ferm->addAfterBoil() ? tr("Yes") : tr("No")) .arg(Measurement::displayQuantity(ferm->yield_pct(), 0) ) - .arg(Measurement::displayAmount(Measurement::Amount{ferm->color_srm(), Measurement::Units::srm}, - PersistentSettings::Sections::fermentableTable, - PropertyNames::Fermentable::color_srm, - 1)); + .arg(Measurement::displayAmount(Measurement::Amount{ferm->color_srm(), Measurement::Units::srm}, 1)); } // One row for the total grain (QTextBrowser does not know the caption tag) ftable += QString("%1%2%3%4%5%6%7") .arg(tr("Total")) .arg("—" ) - .arg(Measurement::displayAmount(Measurement::Amount{rec->grains_kg(), Measurement::Units::kilograms}, - PersistentSettings::Sections::fermentableTable, - PropertyNames::Fermentable::amount)) + .arg(Measurement::displayAmount(Measurement::Amount{rec->grains_kg(), Measurement::Units::kilograms})) .arg("—") .arg("—") .arg("—") @@ -523,16 +491,12 @@ class RecipeFormatter::impl { Fermentable* ferm = ferms[ii]; names.append(ferm->name()); types.append(Fermentable::typeDisplayNames[ferm->type()]); - amounts.append(Measurement::displayAmount(ferm->amountWithUnits(), - PersistentSettings::Sections::fermentableTable, - PropertyNames::Fermentable::amountWithUnits)); + amounts.append(Measurement::displayAmount(ferm->amountWithUnits())); masheds.append( ferm->isMashed() ? tr("Yes") : tr("No")); lates.append( ferm->addAfterBoil() ? tr("Yes") : tr("No")); yields.append( QString("%1%").arg(Measurement::displayQuantity(ferm->yield_pct(), 0) ) ); - colors.append( QString("%1").arg(Measurement::displayAmount(Measurement::Amount{ferm->color_srm(), Measurement::Units::srm}, - PersistentSettings::Sections::fermentableTable, - PropertyNames::Fermentable::color_srm, - 1))); + colors.append( QString("%1").arg(Measurement::displayAmount(Measurement::Amount{ferm->color_srm(), + Measurement::Units::srm}, 1))); } padAllToMaxLength(&names); @@ -548,9 +512,7 @@ class RecipeFormatter::impl { } ret += QString("%1 %2\n").arg(tr("Total grain:")).arg( - Measurement::displayAmount(Measurement::Amount{rec->grains_kg(), Measurement::Units::kilograms}, - PersistentSettings::Sections::fermentableTable, - PropertyNames::Fermentable::amount) + Measurement::displayAmount(Measurement::Amount{rec->grains_kg(), Measurement::Units::kilograms}) ); } return ret; @@ -593,13 +555,9 @@ class RecipeFormatter::impl { hTable += QString("%1%2%%3%4%5%6%7") .arg(hop->name()) .arg(Measurement::displayQuantity(hop->alpha_pct(), 1) ) - .arg(Measurement::displayAmount(Measurement::Amount{hop->amount_kg(), Measurement::Units::kilograms}, - PersistentSettings::Sections::hopTable, - PropertyNames::Hop::amount_kg)) + .arg(Measurement::displayAmount(Measurement::Amount{hop->amount_kg(), Measurement::Units::kilograms})) .arg(Hop::useDisplayNames[hop->use()]) - .arg(Measurement::displayAmount(Measurement::Amount{hop->time_min(), Measurement::Units::minutes}, - PersistentSettings::Sections::hopTable, - PropertyNames::Hop::time_min)) + .arg(Measurement::displayAmount(Measurement::Amount{hop->time_min(), Measurement::Units::minutes})) .arg(Hop::formDisplayNames[hop->form()]) .arg(Measurement::displayQuantity(rec->ibuFromHop(hop), 1) ); } @@ -631,13 +589,10 @@ class RecipeFormatter::impl { names.append(hop->name()); alphas.append(QString("%1%").arg(Measurement::displayQuantity(hop->alpha_pct(), 1))); - amounts.append(Measurement::displayAmount(Measurement::Amount{hop->amount_kg(), Measurement::Units::kilograms}, - PersistentSettings::Sections::hopTable, - PropertyNames::Hop::amount_kg)); + amounts.append(Measurement::displayAmount(Measurement::Amount{hop->amount_kg(), + Measurement::Units::kilograms})); uses.append(Hop::useDisplayNames[hop->use()]); - times.append(Measurement::displayAmount(Measurement::Amount{hop->time_min(), Measurement::Units::minutes}, - PersistentSettings::Sections::hopTable, - PropertyNames::Hop::time_min)); + times.append(Measurement::displayAmount(Measurement::Amount{hop->time_min(), Measurement::Units::minutes})); forms.append(Hop::formDisplayNames[hop->form()]); ibus.append(QString("%1").arg( Measurement::displayQuantity(rec->ibuFromHop(hop), 1))); } @@ -687,19 +642,15 @@ class RecipeFormatter::impl { Misc *misc = miscs[ii]; mtable += QString("%1%2%3%4%5") - .arg( misc->name()) - .arg( misc->typeStringTr()) - .arg( misc->useStringTr()) - .arg( Measurement::displayAmount(Measurement::Amount{ - misc->amount(), - misc->amountIsWeight() ? Measurement::Units::kilograms : Measurement::Units::liters - }, - PersistentSettings::Sections::miscTableModel, - PropertyNames::Misc::amount, - 3)) - .arg( Measurement::displayAmount(Measurement::Amount{misc->time(), Measurement::Units::minutes}, - PersistentSettings::Sections::miscTableModel, - PropertyNames::Misc::time)); + .arg(misc->name()) + .arg(misc->typeStringTr()) + .arg(misc->useStringTr()) + .arg(Measurement::displayAmount(Measurement::Amount{ + misc->amount(), + misc->amountIsWeight() ? Measurement::Units::kilograms : Measurement::Units::liters + }, + 3)) + .arg(Measurement::displayAmount(Measurement::Amount{misc->time(), Measurement::Units::minutes})); } mtable += ""; return mtable; @@ -732,14 +683,9 @@ class RecipeFormatter::impl { Measurement::displayAmount(Measurement::Amount{ misc->amount(), misc->amountIsWeight() ? Measurement::Units::kilograms : Measurement::Units::liters - }, - PersistentSettings::Sections::miscTableModel, - PropertyNames::Misc::amount, - 3) + }, 3) ); - times.append(Measurement::displayAmount(Measurement::Amount{misc->time(), Measurement::Units::minutes}, - PersistentSettings::Sections::miscTableModel, - PropertyNames::Misc::time)); + times.append(Measurement::displayAmount(Measurement::Amount{misc->time(), Measurement::Units::minutes})); } padAllToMaxLength(&names); @@ -792,8 +738,6 @@ class RecipeFormatter::impl { y->amount(), y->amountIsWeight() ? Measurement::Units::kilograms : Measurement::Units::liters }, - PersistentSettings::Sections::yeastTableModel, - PropertyNames::Yeast::amount, 2) ) .arg( y->addToSecondary() ? tr("Secondary") : tr("Primary")); } @@ -827,8 +771,6 @@ class RecipeFormatter::impl { y->amount(), y->amountIsWeight() ? Measurement::Units::kilograms : Measurement::Units::liters }, - PersistentSettings::Sections::yeastTableModel, - PropertyNames::Yeast::amount, 2)); stages.append(y->addToSecondary() ? tr("Secondary") : tr("Primary")); } @@ -881,28 +823,22 @@ class RecipeFormatter::impl { .arg(step->typeStringTr()); if (step->isInfusion()) { - tmp = tmp.arg(Measurement::displayAmount(Measurement::Amount{step->infuseAmount_l(), Measurement::Units::liters}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::infuseAmount_l)) - .arg(Measurement::displayAmount(Measurement::Amount{step->infuseTemp_c(), Measurement::Units::celsius}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::infuseTemp_c)); + tmp = tmp.arg(Measurement::displayAmount(Measurement::Amount{step->infuseAmount_l(), + Measurement::Units::liters})) + .arg(Measurement::displayAmount(Measurement::Amount{step->infuseTemp_c(), + Measurement::Units::celsius})); } else if (step->isDecoction()) { - tmp = tmp.arg( Measurement::displayAmount(Measurement::Amount{step->decoctionAmount_l(), Measurement::Units::liters}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::decoctionAmount_l)) + tmp = tmp.arg( Measurement::displayAmount(Measurement::Amount{step->decoctionAmount_l(), + Measurement::Units::liters})) .arg("---"); } else { tmp = tmp.arg( "---" ).arg("---"); } - tmp = tmp.arg( Measurement::displayAmount(Measurement::Amount{step->stepTemp_c(), Measurement::Units::celsius}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::stepTemp_c) ); - tmp = tmp.arg( Measurement::displayAmount(Measurement::Amount{step->stepTime_min(), Measurement::Units::minutes}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::Misc::time, - 0) ); + tmp = tmp.arg( Measurement::displayAmount(Measurement::Amount{step->stepTemp_c(), + Measurement::Units::celsius})); + tmp = tmp.arg( Measurement::displayAmount(Measurement::Amount{step->stepTime_min(), + Measurement::Units::minutes}, 0)); mtable += tmp + ""; } @@ -936,28 +872,22 @@ class RecipeFormatter::impl { names.append(step->name()); types.append(step->typeStringTr()); if ( step->isInfusion() ) { - amounts.append(Measurement::displayAmount(Measurement::Amount{step->infuseAmount_l(), Measurement::Units::liters}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::infuseAmount_l)); - temps.append(Measurement::displayAmount(Measurement::Amount{step->infuseTemp_c(), Measurement::Units::celsius}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::infuseTemp_c)); + amounts.append(Measurement::displayAmount(Measurement::Amount{step->infuseAmount_l(), + Measurement::Units::liters})); + temps.append(Measurement::displayAmount(Measurement::Amount{step->infuseTemp_c(), + Measurement::Units::celsius})); } else if( step->isDecoction() ) { - amounts.append(Measurement::displayAmount(Measurement::Amount{step->decoctionAmount_l(), Measurement::Units::liters}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::decoctionAmount_l)); + amounts.append(Measurement::displayAmount(Measurement::Amount{step->decoctionAmount_l(), + Measurement::Units::liters})); temps.append("---"); } else { amounts.append( "---" ); temps.append("---"); } - targets.append(Measurement::displayAmount(Measurement::Amount{step->stepTemp_c(), Measurement::Units::celsius}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::stepTemp_c)); - times.append(Measurement::displayAmount(Measurement::Amount{step->stepTime_min(), Measurement::Units::minutes}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::Misc::time, - 0)); + targets.append(Measurement::displayAmount(Measurement::Amount{step->stepTemp_c(), + Measurement::Units::celsius})); + times.append(Measurement::displayAmount(Measurement::Amount{step->stepTime_min(), + Measurement::Units::minutes}, 0)); } padAllToMaxLength(&names); @@ -1073,33 +1003,25 @@ class RecipeFormatter::impl { bnTable += QString("%1").arg(tr("Preboil")); bnTable += QString("%1%2%3%4") .arg(tr("SG")) - .arg(Measurement::displayAmount(Measurement::Amount{note->sg(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::page_preboil, - PropertyNames::BrewNote::sg, - 3)) + .arg(Measurement::displayAmount(Measurement::Amount{note->sg(), Measurement::Units::sp_grav}, 3)) .arg(tr("Volume into BK")) - .arg(Measurement::displayAmount(Measurement::Amount{note->volumeIntoBK_l(), Measurement::Units::liters}, - PersistentSettings::Sections::page_preboil, - PropertyNames::BrewNote::volumeIntoBK_l)); + .arg(Measurement::displayAmount(Measurement::Amount{note->volumeIntoBK_l(), + Measurement::Units::liters})); bnTable += QString("%1%2%3%4") .arg(tr("Strike Temp")) - .arg(Measurement::displayAmount(Measurement::Amount{note->strikeTemp_c(), Measurement::Units::celsius}, - PersistentSettings::Sections::page_preboil, - PropertyNames::BrewNote::strikeTemp_c)) + .arg(Measurement::displayAmount(Measurement::Amount{note->strikeTemp_c(), + Measurement::Units::celsius})) .arg(tr("Final Temp")) - .arg(Measurement::displayAmount(Measurement::Amount{note->mashFinTemp_c(), Measurement::Units::celsius}, - PersistentSettings::Sections::page_preboil, - PropertyNames::BrewNote::mashFinTemp_c)); + .arg(Measurement::displayAmount(Measurement::Amount{note->mashFinTemp_c(), + Measurement::Units::celsius})); bnTable += QString("%1%2%%3%4") .arg(tr("Eff into BK")) .arg(Measurement::displayQuantity(note->calculateEffIntoBK_pct(), 2)) .arg(tr("Projected OG")) - .arg(Measurement::displayAmount(Measurement::Amount{note->calculateOg(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::page_preboil, - PropertyNames::BrewNote::projOg, - 3)); + .arg(Measurement::displayAmount(Measurement::Amount{note->calculateOg(), + Measurement::Units::sp_grav}, 3)); bnTable += ""; // POSTBOIL @@ -1107,19 +1029,14 @@ class RecipeFormatter::impl { bnTable += QString("%1").arg(tr("Postboil")); bnTable += QString("%1%2%3%4") .arg(tr("OG")) - .arg(Measurement::displayAmount(Measurement::Amount{note->og(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::page_postboil, - PropertyNames::BrewNote::og, - 3)) + .arg(Measurement::displayAmount(Measurement::Amount{note->og(), Measurement::Units::sp_grav}, 3)) .arg(tr("Postboil Volume")) - .arg(Measurement::displayAmount(Measurement::Amount{note->postBoilVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::page_postboil, - PropertyNames::BrewNote::postBoilVolume_l)); + .arg(Measurement::displayAmount(Measurement::Amount{note->postBoilVolume_l(), + Measurement::Units::liters})); bnTable += QString("%1%2%3%4") .arg(tr("Volume Into Fermenter")) - .arg(Measurement::displayAmount(Measurement::Amount{note->volumeIntoFerm_l(), Measurement::Units::liters}, - PersistentSettings::Sections::page_postboil, - PropertyNames::BrewNote::volumeIntoFerm_l)) + .arg(Measurement::displayAmount(Measurement::Amount{note->volumeIntoFerm_l(), + Measurement::Units::liters})) .arg(tr("Brewhouse Eff")) .arg(Measurement::displayQuantity(note->calculateBrewHouseEff_pct(), 2)); bnTable += QString("%1%2%") @@ -1133,14 +1050,9 @@ class RecipeFormatter::impl { bnTable += QString("%1").arg(tr("Postferment")); bnTable += QString("%1%2%3%4") .arg(tr("FG")) - .arg(Measurement::displayAmount(Measurement::Amount{note->fg(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::page_postferment, - PropertyNames::BrewNote::fg, - 3)) + .arg(Measurement::displayAmount(Measurement::Amount{note->fg(), Measurement::Units::sp_grav}, 3)) .arg(tr("Volume")) - .arg(Measurement::displayAmount(Measurement::Amount{note->finalVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::page_postferment, - PropertyNames::BrewNote::finalVolume_l)); + .arg(Measurement::displayAmount(Measurement::Amount{note->finalVolume_l(), Measurement::Units::liters})); bnTable += QString("%1%2%3%4") .arg(tr("Date")) diff --git a/src/RefractoDialog.cpp b/src/RefractoDialog.cpp index c9d8c613..eff18779 100644 --- a/src/RefractoDialog.cpp +++ b/src/RefractoDialog.cpp @@ -32,22 +32,23 @@ RefractoDialog::RefractoDialog(QWidget* parent) : QDialog(parent) { setupUi(this); - SMART_LINE_EDIT_INIT_FS_FIXED(RefractoDialog, lineEdit_op , double, Measurement::PhysicalQuantity::Density , 1); // Original Plato - SMART_LINE_EDIT_INIT_FS_FIXED(RefractoDialog, lineEdit_inputOG, double, Measurement::PhysicalQuantity::Density , 3); // Original gravity in - SMART_LINE_EDIT_INIT_FS_FIXED(RefractoDialog, lineEdit_cp , double, Measurement::PhysicalQuantity::Density , 1); // Current Plato - SMART_LINE_EDIT_INIT_FS_FIXED(RefractoDialog, lineEdit_ri , double, NonPhysicalQuantity::Dimensionless ); // Refractive index - SMART_LINE_EDIT_INIT_FS_FIXED(RefractoDialog, lineEdit_og , double, Measurement::PhysicalQuantity::Density , 3); // Original gravity out - SMART_LINE_EDIT_INIT_FS_FIXED(RefractoDialog, lineEdit_sg , double, Measurement::PhysicalQuantity::Density , 3); // Specific gravity out - SMART_LINE_EDIT_INIT_FS_FIXED(RefractoDialog, lineEdit_abv , double, NonPhysicalQuantity::Percentage ); // Alcohol by volume - SMART_LINE_EDIT_INIT_FS_FIXED(RefractoDialog, lineEdit_abw , double, NonPhysicalQuantity::Percentage ); // Alcohol by weight - SMART_LINE_EDIT_INIT_FS_FIXED(RefractoDialog, lineEdit_re , double, Measurement::PhysicalQuantity::Density , 1); // Real extract Plato - - this->lineEdit_op ->getUiAmountWithUnits().setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::Plato ); - this->lineEdit_inputOG->getUiAmountWithUnits().setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); - this->lineEdit_cp ->getUiAmountWithUnits().setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::Plato ); - this->lineEdit_og ->getUiAmountWithUnits().setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); - this->lineEdit_sg ->getUiAmountWithUnits().setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); - this->lineEdit_re ->getUiAmountWithUnits().setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::Plato ); + // Note that the labels here are QLabel, not SmartLabel, because we want the units fixed, not user-selectable + SMART_FIELD_INIT_FS(RefractoDialog, label_op , lineEdit_op , double, Measurement::PhysicalQuantity::Density , 1); // Original Plato + SMART_FIELD_INIT_FS(RefractoDialog, label_inputOG, lineEdit_inputOG, double, Measurement::PhysicalQuantity::Density , 3); // Original gravity in + SMART_FIELD_INIT_FS(RefractoDialog, label_cp , lineEdit_cp , double, Measurement::PhysicalQuantity::Density , 1); // Current Plato + SMART_FIELD_INIT_FS(RefractoDialog, label_ri , lineEdit_ri , double, NonPhysicalQuantity::Dimensionless ); // Refractive index + SMART_FIELD_INIT_FS(RefractoDialog, label_og , lineEdit_og , double, Measurement::PhysicalQuantity::Density , 3); // Original gravity out + SMART_FIELD_INIT_FS(RefractoDialog, label_sg , lineEdit_sg , double, Measurement::PhysicalQuantity::Density , 3); // Specific gravity out + SMART_FIELD_INIT_FS(RefractoDialog, label_abv , lineEdit_abv , double, NonPhysicalQuantity::Percentage ); // Alcohol by volume + SMART_FIELD_INIT_FS(RefractoDialog, label_abw , lineEdit_abw , double, NonPhysicalQuantity::Percentage ); // Alcohol by weight + SMART_FIELD_INIT_FS(RefractoDialog, label_re , lineEdit_re , double, Measurement::PhysicalQuantity::Density , 1); // Real extract Plato + + this->lineEdit_op ->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::Plato ); + this->lineEdit_inputOG->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); + this->lineEdit_cp ->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::Plato ); + this->lineEdit_og ->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); + this->lineEdit_sg ->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); + this->lineEdit_re ->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::Plato ); connect(this->pushButton_calculate, &QAbstractButton::clicked, this, &RefractoDialog::calculate ); return; diff --git a/src/SmartAmounts.cpp b/src/SmartAmounts.cpp new file mode 100644 index 00000000..d5d8e782 --- /dev/null +++ b/src/SmartAmounts.cpp @@ -0,0 +1,171 @@ +/*====================================================================================================================== + * SmartAmounts.cpp is part of Brewken, and is copyright the following authors 2023: + * • Matt Young + * + * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see + * . + =====================================================================================================================*/ +#include "SmartAmounts.h" + +#include + +#include "measurement/Measurement.h" +#include "PersistentSettings.h" +#include "utils/TypeLookup.h" +#include "widgets/SmartLabel.h" +#include "SmartField.h" + +template<> void SmartAmounts::Init(char const * const editorName, + char const * const labelName, + char const * const labelFqName, + SmartLabel & label, + char const * const fieldName, + char const * const fieldlFqName, + SmartField & field, + TypeInfo const & typeInfo, + std::optional const precision, + QString const & maximalDisplayString) { + label.init(editorName, labelName, labelFqName, &field, typeInfo); + field.init(editorName, fieldName, fieldlFqName, label, typeInfo, precision, maximalDisplayString); + return; +} + +template<> void SmartAmounts::Init(char const * const editorName, + [[maybe_unused]] char const * const labelName, + [[maybe_unused]] char const * const labelFqName, + QLabel & label, + char const * const fieldName, + char const * const fieldlFqName, + SmartField & field, + TypeInfo const & typeInfo, + std::optional const precision, + QString const & maximalDisplayString) { + field.init(editorName, fieldName, fieldlFqName, label, typeInfo, precision, maximalDisplayString); + return; +} + +void SmartAmounts::InitNoSle(char const * const editorName, + char const * const labelName, + char const * const labelFqName, + SmartLabel & label, + TypeInfo const & typeInfo, + std::optional const precision, + QString const & maximalDisplayString) { + label.init(editorName, labelName, labelFqName, nullptr, typeInfo); + return; +} + +void SmartAmounts::InitFixed(char const * const editorName, + QLabel & label, + char const * const fieldName, + char const * const fieldlFqName, + SmartField & field, + TypeInfo const & typeInfo, + Measurement::Unit const & fixedDisplayUnit, + std::optional const precision, + QString const & maximalDisplayString) { + field.initFixed(editorName, + fieldName, + fieldlFqName, + label, + typeInfo, + fixedDisplayUnit, + precision, + maximalDisplayString); + return; +} + +void SmartAmounts::setForcedSystemOfMeasurement(char const * const owningWindowName, + char const * const fieldName, + std::optional forcedSystemOfMeasurement) { + if (forcedSystemOfMeasurement) { + PersistentSettings::insert(fieldName, + Measurement::getUniqueName(*forcedSystemOfMeasurement), + owningWindowName, + PersistentSettings::Extension::UNIT); + } else { + PersistentSettings::remove(fieldName, + owningWindowName, + PersistentSettings::Extension::UNIT); + } + return; +} + +void SmartAmounts::setForcedRelativeScale(char const * const owningWindowName, + char const * const fieldName, + std::optional forcedScale) { + if (forcedScale) { + PersistentSettings::insert(fieldName, + Measurement::UnitSystem::getUniqueName(*forcedScale), + owningWindowName, + PersistentSettings::Extension::SCALE); + } else { + PersistentSettings::remove(fieldName, + owningWindowName, + PersistentSettings::Extension::SCALE); + } + return; +} + +std::optional SmartAmounts::getForcedSystemOfMeasurement(char const * const owningWindowName, + char const * const fieldName) { + return Measurement::getFromUniqueName( + PersistentSettings::value(fieldName, + "None", // This, or any invalid name, will give "no value" return from getFromUniqueName() + owningWindowName, + PersistentSettings::Extension::UNIT).toString() + ); +} + +std::optional SmartAmounts::getForcedRelativeScale(char const * const owningWindowName, + char const * const fieldName) { + return Measurement::UnitSystem::getScaleFromUniqueName( + PersistentSettings::value(fieldName, + "None", // This, or any invalid name, will give "no value" return from getFromUniqueName() + owningWindowName, + PersistentSettings::Extension::SCALE).toString() + ); +} + +Measurement::SystemOfMeasurement SmartAmounts::getSystemOfMeasurement(char const * const owningWindowName, + char const * const fieldName, + Measurement::PhysicalQuantities physicalQuantities) { + auto forcedSystemOfMeasurement = SmartAmounts::getForcedSystemOfMeasurement(owningWindowName, fieldName); + if (forcedSystemOfMeasurement) { + return *forcedSystemOfMeasurement; + } + + Measurement::PhysicalQuantity const physicalQuantity = + std::holds_alternative(physicalQuantities) ? + std::get(physicalQuantities) : + std::get<0>(std::get(physicalQuantities)); + + return Measurement::getDisplayUnitSystem(physicalQuantity).systemOfMeasurement; +} + +Measurement::UnitSystem const & SmartAmounts::getUnitSystem(char const * const owningWindowName, + char const * const fieldName, + Measurement::PhysicalQuantity physicalQuantity) { + auto forcedSystemOfMeasurement = SmartAmounts::getForcedSystemOfMeasurement(owningWindowName, fieldName); + if (forcedSystemOfMeasurement) { + return Measurement::UnitSystem::getInstance(*forcedSystemOfMeasurement, physicalQuantity); + } + return Measurement::getDisplayUnitSystem(physicalQuantity); +} + +SmartAmounts::ScaleInfo SmartAmounts::getScaleInfo(char const * const owningWindowName, + char const * const fieldName, + Measurement::PhysicalQuantities physicalQuantities) { + return SmartAmounts::ScaleInfo{ + SmartAmounts::getSystemOfMeasurement(owningWindowName, fieldName, physicalQuantities), + SmartAmounts::getForcedRelativeScale(owningWindowName, fieldName) + }; +} diff --git a/src/SmartAmounts.h b/src/SmartAmounts.h new file mode 100644 index 00000000..f0bd5daa --- /dev/null +++ b/src/SmartAmounts.h @@ -0,0 +1,272 @@ +/*====================================================================================================================== + * SmartAmounts.h is part of Brewken, and is copyright the following authors 2023: + * • Matt Young + * + * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see + * . + =====================================================================================================================*/ +#ifndef SMARTAMOUNTS_H +#define SMARTAMOUNTS_H +#pragma once + +#include + +#include + +#include "measurement/SystemOfMeasurement.h" +#include "measurement/UnitSystem.h" + +class QLabel; + +class SmartLabel; +class SmartField; +class TypeInfo; + +namespace SmartAmounts { + /** + * \brief Initialise a \c SmartLabel or \c QLabel and one of its \c SmartField buddies. In the edge case that + * there are additional \c SmartField buddies, then this function should be called multiple times. + * + * See comment in \c widgets/SmartLabel.h for more details. + */ + template + void Init(char const * const editorName, + char const * const labelName, + char const * const labelFqName, + LabelType & label, + char const * const fieldName, + char const * const fieldlFqName, + SmartField & field, + TypeInfo const & typeInfo, + std::optional const precision = std::nullopt, + QString const & maximalDisplayString = "100.000 srm"); + + /** + * \brief Alternate version of \c SmartAmounts::Init for when there is a \c SmartLabel but no \c SmartField + */ + void InitNoSle(char const * const editorName, + char const * const labelName, + char const * const labelFqName, + SmartLabel & label, + TypeInfo const & typeInfo, + std::optional const precision = std::nullopt, + QString const & maximalDisplayString = "100.000 srm"); + + /** + * \brief Alternate version of \c SmartAmounts::Init for when units are fixed. (Note that this implies label is + * \c QLabel, not \c SmartLabel.) + */ + void InitFixed(char const * const editorName, + QLabel & label, + char const * const fieldName, + char const * const fieldlFqName, + SmartField & field, + TypeInfo const & typeInfo, + Measurement::Unit const & fixedDisplayUnit, + std::optional const precision = std::nullopt, + QString const & maximalDisplayString = "100.000 srm"); + + /** + * \brief Called by \c SmartLabel or subclasses of \c BtTableModel + * + * \param owningWindowName The class name of the editor (eg \c HopEditor) or \c BtTableModel (eg \c HopTableModel) + * \param fieldName The name of the member variable for a \c SmartLabel or of a \c ColumnIndex enum for a + * \c BtTableModel column + */ + void setForcedSystemOfMeasurement(char const * const owningWindowName, + char const * const fieldName, + std::optional forcedSystemOfMeasurement); + + /** + * \brief Similar to setForcedSystemOfMeasurement + */ + void setForcedRelativeScale(char const * const owningWindowName, + char const * const fieldName, + std::optional forcedScale); + + /** + * \brief Called by \c SmartLabel or subclasses of \c BtTableModel + * + * \param owningWindowName The class name of the editor (eg \c HopEditor) or \c BtTableModel (eg \c HopTableModel) + * \param fieldName The name of the member variable for a \c SmartLabel or of a \c ColumnIndex enum for a + * \c BtTableModel column + */ + std::optional getForcedSystemOfMeasurement(char const * const owningWindowName, + char const * const fieldName); + + /** + * \brief Similar to getForcedSystemOfMeasurement + */ + std::optional getForcedRelativeScale(char const * const owningWindowName, + char const * const fieldName); + + /** + * \brief Returns the forced \c SystemOfMeasurement or, if there is none, the \c SystemOfMeasurement of the + * globally-set \c UnitSystem for the \c PhysicalQuantity -- except that, if there are two values of + * \c PhysicalQuantity, we have to choose one arbitrarily. The end result should be the same, because \c Mass + * & \c Volume share the same \c SystemOfMeasurement, as do \c MassConcentration & \c VolumeConcentration. + */ + Measurement::SystemOfMeasurement getSystemOfMeasurement(char const * const owningWindowName, + char const * const fieldName, + Measurement::PhysicalQuantities physicalQuantities); + + /** + * + */ + Measurement::UnitSystem const & getUnitSystem(char const * const owningWindowName, + char const * const fieldName, + Measurement::PhysicalQuantity physicalQuantity); + + /** + * \brief It's sometimes useful to have \c SystemOfMeasurement and \c UnitSystem::RelativeScale in a single variable + */ + struct ScaleInfo { + Measurement::SystemOfMeasurement systemOfMeasurement; + std::optional relativeScale = std::nullopt; + }; + + /** + * \brief Effectively combines calls to \c getSystemOfMeasurement and \c getForcedRelativeScale + */ + ScaleInfo getScaleInfo(char const * const owningWindowName, + char const * const fieldName, + Measurement::PhysicalQuantities physicalQuantities); + +} + +/** + * \brief Helper macro for \c SMART_FIELD_INIT. Essentially does concatenation, using the magic, that, for the + * compiler, there is no difference between writing a string literal as: + * "foobarhumbug" + * and writing it as: + * "foo" "bar" "humbug" + */ +#define SFI_FQ_NAME(editorClass, fieldName) #editorClass "->" #fieldName + +/** + * \brief This macro saves a bit of copy-and-paste when invoking \c SmartFieldInit. Eg instead of writing: + * + * SmartFieldInit("FermentableEditor", + * "label_color", + * "FermentableEditor->label_color", + * this->label_color, + * "field_color", + * "FermentableEditor->field_color", + * this->field_color, + * Fermentable::typeLookup.getType(PropertyNames::Fermentable::color_srm), + * 0); + * + * you write: + * + * SMART_FIELD_INIT(FermentableEditor, label_color, field_color, Fermentable, PropertyNames::Fermentable::color_srm, 0); + * + * \param editorClass The class name of the class holding the field we're initialising, eg \c HopEditor. (In theory we + * could pick this up via \c staticMetaObject.className(), but then we wouldn't be able to do the + * macro concatenation here.) + * \param modelClass The subclass of \c NamedEntity that we're editing. Eg in \c HopEditor, this will be \c Hop + * \param labelName The name of the member variable for the corresponding label (\c QLabel or \c SmartLabel) for this + * field. NB we cannot always deduce this from fieldName, as sometimes two fields share a label, eg + * for a min/max range on a \c Style. + * \param fieldName The name of the member variable for this field, eg in \c HopEditor, this could be \c field_name, + * \c field_alpha, etc. Note that: + * - We deliberately don't try to do anything clever with automatically inserting the "field_" + * prefix, as this would make the code harder to read and search. + * - It is intentional that field names sometimes differ slightly from property names. The latter + * always include their canonical unit names (eg \c PropertyNames::Fermentable::color_srm) whereas + * the former do not (eg \c field_color) because the user can enter data in any supported + * units. + * \param propertyName The name of the property to which this field relates, eg in \c HopEditor, this could be + * \c PropertyNames::NamedEntity::name, \c PropertyNames::Hop::alpha_pct, etc. (Note, as above, we + * intentionally do \b not automatically insert the \c PropertyNames:: prefix.) + * \param ... Any remaining arguments are passed through to \c SmartField::init in fourth position and above + * Note that the introduction of __VA_OPT__ in C++20 makes our lives easier here. + */ +#define SMART_FIELD_INIT(editorClass, labelName, fieldName, modelClass, propertyName, ...) \ + SmartAmounts::Init(#editorClass, \ + #labelName, \ + SFI_FQ_NAME(editorClass, labelName), \ + *this->labelName, \ + #fieldName, \ + SFI_FQ_NAME(editorClass, fieldName), \ + *this->fieldName, \ + modelClass ::typeLookup.getType(propertyName) \ + __VA_OPT__(, __VA_ARGS__)) + +/** + * \brief Alternate version of \c SMART_FIELD_INIT for when there is no \c SmartField + */ +#define SMART_FIELD_INIT_NO_SLE(editorClass, labelName, modelClass, propertyName, ...) \ + SmartAmounts::InitNoSle(#editorClass, \ + #labelName, \ + SFI_FQ_NAME(editorClass, labelName), \ + *this->labelName, \ + modelClass ::typeLookup.getType(propertyName) \ + __VA_OPT__(, __VA_ARGS__)) + +/** + * \brief An alternate version of \c SMART_FIELD_INIT for use when there is no \c modelClass (eg in a free-standing + * calculation dialog that does not update the model). Instead of writing: + * + * static auto const typeInfoFor_field_temp = TypeInfo::construct(Measurement::PhysicalQuantity::Temperature); + * this->field_temp->init("PrimingDialog->field_temp", typeInfoFor_field_temp, *this->label_temp, 1); + * SmartFieldInit("PrimingDialog", + * "label_temp", + * "PrimingDialog->label_temp", + * this->label_temp, + * "field_temp", + * "PrimingDialog->field_temp", + * this->field_temp, + * typeInfoFor_field_temp, + * 1); + * + * you write: + * + * SMART_FIELD_INIT_FS(PrimingDialog, label_temp, field_temp, double, Measurement::PhysicalQuantity::Temperature, 1); + * + * The _FS in the name stands for "free-standing". + * + * \param editorClass As for \c SMART_FIELD_INIT. + * \param labelName As for \c SMART_FIELD_INIT + * \param fieldName As for \c SMART_FIELD_INIT + * \param nativeType The native type in which this value is / would be stored, eg double + * \param btFieldType The \c BtFieldType for this field. Together with \c nativeType, this is used to construct a + * static local \c TypeInfo struct to pass by reference to \c SmartField::init. + * \param ... Any remaining arguments are passed through to \c SmartField::init in fourth position and above + */ +#define SMART_FIELD_INIT_FS(editorClass, labelName, fieldName, nativeType, btFieldType, ...) \ + static auto const typeInfoFor_##fieldName = TypeInfo::construct(btFieldType); \ + SmartAmounts::Init(#editorClass, \ + #labelName, \ + SFI_FQ_NAME(editorClass, labelName), \ + *this->labelName, \ + #fieldName, \ + SFI_FQ_NAME(editorClass, fieldName), \ + *this->fieldName, \ + typeInfoFor_##fieldName \ + __VA_OPT__(, __VA_ARGS__)) + + +/** + * \brief An alternate version of \c SMART_FIELD_INIT_FS for use when there is no \c modelClass and display units are + * fixed. + */ +#define SMART_FIELD_INIT_FIXED(editorClass, labelName, fieldName, nativeType, fixedUnit, ...) \ + static auto const typeInfoFor_##fieldName = TypeInfo::construct(fixedUnit.getPhysicalQuantity()); \ + SmartAmounts::InitFixed(#editorClass, \ + *this->labelName, \ + #fieldName, \ + SFI_FQ_NAME(editorClass, fieldName), \ + *this->fieldName, \ + typeInfoFor_##fieldName, \ + fixedUnit \ + __VA_OPT__(, __VA_ARGS__)) + +#endif diff --git a/src/SmartField.cpp b/src/SmartField.cpp new file mode 100644 index 00000000..bd606e07 --- /dev/null +++ b/src/SmartField.cpp @@ -0,0 +1,673 @@ +/*====================================================================================================================== + * SmartField.cpp is part of Brewken, and is copyright the following authors 2009-2023: + * • Brian Rower + * • Mark de Wever + * • Mattias Måhl + * • Matt Young + * • Mike Evans + * • Mik Firestone + * • Philip Greggory Lee + * • Théophane Martin + * + * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see + * . + =====================================================================================================================*/ +#include "SmartField.h" + +#include + +#include +#include +#include + +#include "Localization.h" +#include "measurement/Measurement.h" +#include "utils/OptionalHelpers.h" +#include "utils/TypeLookup.h" +#include "widgets/SmartLabel.h" + +// This private implementation class holds all private non-virtual members of SmartField +class SmartField::impl { +public: + impl(SmartField & self) : + m_self {self}, + m_initialised {false}, + m_editorName {"Uninitialised m_editorName!" }, + m_fieldName {"Uninitialised m_fieldName!" }, + m_fieldFqName {"Uninitialised m_fieldFqName!"}, + m_typeInfo {nullptr}, + m_fixedDisplayUnit {nullptr}, + m_smartBuddyLabel {nullptr}, + m_currentPhysicalQuantity{std::nullopt}, + m_precision {3}, + m_maximalDisplayString {"100.000 srm"} { + return; + } + + ~impl() = default; + + /** + * \brief We want to have two different signatures of \c SmartField::init so we can catch missing parameters at + * compile time. Ultimately they both do pretty much the same work, by calling this function. + */ + void init(char const * const editorName, + char const * const fieldName, + char const * const fieldFqName, + SmartLabel * smartBuddyLabel, + TypeInfo const & typeInfo, + Measurement::Unit const * fixedDisplayUnit, + std::optional const precision, + QString const & maximalDisplayString) { + // It's a coding error to call this function twice on the same object, ie we should only initialise something + // once! + Q_ASSERT(!this->m_initialised); + + this->m_editorName = editorName; + this->m_fieldName = fieldName; + this->m_fieldFqName = fieldFqName; + this->m_typeInfo = &typeInfo; + this->m_fixedDisplayUnit = fixedDisplayUnit; + this->m_smartBuddyLabel = smartBuddyLabel; + + // It's a coding error to have both a smartBuddyLabel and a fixedDisplayUnit + Q_ASSERT(!this->m_fixedDisplayUnit || !this->m_smartBuddyLabel); + + if (precision) { + // It's a coding error to specify precision for a field that's not a (possibly optional) double (or a float, + // but we don't use float). However, we allow precision of 0 for a type that is stored as an int or unsigned + // int. + Q_ASSERT(this->m_typeInfo->typeIndex == typeid(double) || + this->m_typeInfo->typeIndex == typeid(std::optional) || + (0 == *precision && this->m_typeInfo->typeIndex == typeid(int )) || + (0 == *precision && this->m_typeInfo->typeIndex == typeid(unsigned int)) ); + + if (this->m_typeInfo->typeIndex == typeid(double) || + this->m_typeInfo->typeIndex == typeid(std::optional)) { + // It's a coding error if precision is not some plausible value. For the moment at least, we assert there + // are no envisageable circumstances where we need to show more than 3 decimal places + Q_ASSERT(*precision <= 3); + this->m_precision = *precision; + } + } + // For integers, there are no decimal places to show + if (this->m_typeInfo->typeIndex == typeid(int) || + this->m_typeInfo->typeIndex == typeid(unsigned int)) { + this->m_precision = 0; + } + this->m_maximalDisplayString = maximalDisplayString; + this->m_initialised = true; + + if (std::holds_alternative(*this->m_typeInfo->fieldType)) { + // It's a coding error to have either a smartBuddyLabel or a fixedDisplayUnit for a NonPhysicalQuantity + Q_ASSERT(!this->m_fixedDisplayUnit); + Q_ASSERT(!this->m_smartBuddyLabel); + } else { + // It's only meaningful to have a SmartBuddyLabel if we are dealing with a PhysicalQuantity, but it's not + // required to have one if the scale and units are not changeable by the user. + if (this->m_smartBuddyLabel) { + this->m_self.connectSmartLabelSignal(*this->m_smartBuddyLabel); + } + + if (std::holds_alternative(*this->m_typeInfo->fieldType)) { + // If there is a choice of physical quantities (eg MassOrVolume) then start off with the first one + this->m_currentPhysicalQuantity = std::get<0>(std::get(*this->m_typeInfo->fieldType)); + } else { + this->m_currentPhysicalQuantity = std::get(*this->m_typeInfo->fieldType); + } + } + + // Now let our subclass (SmartLineEdit, SmartDigitWidget, etc) do any of its own initialisation + this->m_self.doPostInitWork(); + + return; + } + + /** + * \brief Returns the contents of the field converted, if necessary, to SI units + * + * \param enteredText + * \param previousScaleInfo + */ + Measurement::Amount toCanonical(QString const & enteredText, SmartAmounts::ScaleInfo previousScaleInfo) { + Q_ASSERT(this->m_initialised); + + // It's a coding error to call this for a NonPhysicalQuantity + Q_ASSERT(!std::holds_alternative(*this->m_typeInfo->fieldType)); + Q_ASSERT(this->m_currentPhysicalQuantity); + + qDebug() << + Q_FUNC_INFO << "enteredText:" << enteredText << ", old SystemOfMeasurement:" << + previousScaleInfo.systemOfMeasurement << ", old RelativeScale: " << previousScaleInfo.relativeScale; + + Measurement::UnitSystem const & oldUnitSystem = + Measurement::UnitSystem::getInstance(previousScaleInfo.systemOfMeasurement, + *this->m_currentPhysicalQuantity); + + Measurement::Unit const * defaultUnit{ + previousScaleInfo.relativeScale ? oldUnitSystem.scaleUnit(*previousScaleInfo.relativeScale) : oldUnitSystem.unit() + }; + + // It's a coding error if defaultUnit is null, because it means previousScaleInfo.relativeScale was not valid for + // oldUnitSystem. However, we can recover. + if (!defaultUnit) { + qWarning() << Q_FUNC_INFO << "previousScaleInfo.relativeScale invalid?" << previousScaleInfo.relativeScale; + defaultUnit = oldUnitSystem.unit(); + } + + // + // Normally, we display units with the text. If the user just edits the number, then the units will still be there. + // Alternatively, if the user specifies different units in the text, we should try to honour those. Otherwise, if, + // no units are specified in the text, we need to go to defaults. Defaults are either what is "forced" for this + // specific field or, failing that, what is configured globally. + // + // Measurement::UnitSystem::qStringToSI will handle all the logic to deal with any units specified by the user in the + // string. (In theory, we just grab the units that the user has specified in the input text. In reality, it's not + // that easy as we sometimes need to disambiguate - eg between Imperial gallons and US customary ones. So, if we + // have old or current units then that helps with this - eg, if current units are US customary cups and user enters + // gallons, then we'll go with US customary gallons over Imperial ones.) + // + auto amount = oldUnitSystem.qstringToSI(enteredText, *defaultUnit); + qDebug() << Q_FUNC_INFO << "Converted to" << amount; + return amount; + } + + SmartField & m_self; + bool m_initialised; + char const * m_editorName; + char const * m_fieldName; + char const * m_fieldFqName; + TypeInfo const * m_typeInfo; + Measurement::Unit const * m_fixedDisplayUnit; + SmartLabel * m_smartBuddyLabel; + // If m_typeInfo->fieldType is a Mixed2PhysicalQuantities (eg PqEitherMassOrVolume), this is where we store which of + // the two PhysicalQuantity values (eg Mass or Volume) is currently set. If m_typeInfo->fieldType is a + // PhysicalQuantity, then this will just be a copy of it. + std::optional m_currentPhysicalQuantity; + // "Precision" (ie number of decimal places to show) is used if and only the field is numeric. For int and unsigned + // int, it must always be 0. + unsigned int m_precision; + QString m_maximalDisplayString; +}; + + +SmartField::SmartField() : pimpl{std::make_unique(*this)} { + return; +} + +SmartField::~SmartField() = default; + +template<> void SmartField::init(char const * const editorName, + char const * const fieldName, + char const * const fieldFqName, + SmartLabel & smartBuddyLabel, + TypeInfo const & typeInfo, + std::optional const precision, + QString const & maximalDisplayString) { + qDebug() << Q_FUNC_INFO << fieldFqName << ":" << typeInfo; + + // It's a coding error to call this version of init with a NonPhysicalQuantity + Q_ASSERT(typeInfo.fieldType && !std::holds_alternative(*typeInfo.fieldType)); + + this->pimpl->init(editorName, + fieldName, + fieldFqName, + &smartBuddyLabel, + typeInfo, + nullptr, + precision, + maximalDisplayString); + return; +} + +template<> void SmartField::init(char const * const editorName, + char const * const fieldName, + char const * const fieldFqName, + QLabel & regularBuddyLabel, + TypeInfo const & typeInfo, + std::optional const precision, + QString const & maximalDisplayString) { + qDebug() << Q_FUNC_INFO << fieldFqName << ":" << typeInfo; + + // It's a coding error to call this version of init with a PhysicalQuantity + Q_ASSERT(typeInfo.fieldType && std::holds_alternative(*typeInfo.fieldType)); + + this->pimpl->init(editorName, + fieldName, + fieldFqName, + nullptr, + typeInfo, + nullptr, + precision, + maximalDisplayString); + return; +} + +void SmartField::initFixed(char const * const editorName, + char const * const fieldName, + char const * const fieldFqName, + QLabel & buddyLabel, + TypeInfo const & typeInfo, + Measurement::Unit const & fixedDisplayUnit, + std::optional const precision, + QString const & maximalDisplayString) { + qDebug() << Q_FUNC_INFO << fieldFqName << ":" << typeInfo; + + // It's a coding error to call this version of init with a NonPhysicalQuantity + Q_ASSERT(typeInfo.fieldType && !std::holds_alternative(*typeInfo.fieldType)); + + this->pimpl->init(editorName, + fieldName, + fieldFqName, + nullptr, + typeInfo, + &fixedDisplayUnit, + precision, + maximalDisplayString); + return; +} + +[[nodiscard]] bool SmartField::isInitialised() const { + return this->pimpl->m_initialised; +} + +BtFieldType const SmartField::getFieldType() const { + Q_ASSERT(this->pimpl->m_initialised); + return *this->pimpl->m_typeInfo->fieldType; +} + +TypeInfo const & SmartField::getTypeInfo() const { + Q_ASSERT(this->pimpl->m_initialised); + return *this->pimpl->m_typeInfo; +} + +QString const & SmartField::getMaximalDisplayString() const { + return this->pimpl->m_maximalDisplayString; +} + +char const * SmartField::getFqFieldName() const { + return this->pimpl->m_fieldFqName; +} + +Measurement::Amount SmartField::toCanonical() const { + Q_ASSERT(this->pimpl->m_initialised); + return this->pimpl->toCanonical(this->getRawText(), this->getScaleInfo()); +} + +void SmartField::setForcedSystemOfMeasurement(std::optional forcedSystemOfMeasurement) { + Q_ASSERT(this->pimpl->m_initialised); + // It's a coding error to call this when we have a fixed display unit + Q_ASSERT(!this->pimpl->m_fixedDisplayUnit); + + if (this->pimpl->m_smartBuddyLabel) { + this->pimpl->m_smartBuddyLabel->setForcedSystemOfMeasurement(forcedSystemOfMeasurement); + } else { + SmartAmounts::setForcedSystemOfMeasurement(this->pimpl->m_editorName, + this->pimpl->m_fieldName, + forcedSystemOfMeasurement); + } + return; +} + +void SmartField::setForcedRelativeScale(std::optional forcedScale) { + Q_ASSERT(this->pimpl->m_initialised); + // It's a coding error to call this when we have a fixed display unit + Q_ASSERT(!this->pimpl->m_fixedDisplayUnit); + + if (this->pimpl->m_smartBuddyLabel) { + this->pimpl->m_smartBuddyLabel->setForcedRelativeScale(forcedScale); + } else { + SmartAmounts::setForcedRelativeScale(this->pimpl->m_editorName, this->pimpl->m_fieldName, forcedScale); + } + return; +} + +std::optional SmartField::getForcedSystemOfMeasurement() const { + Q_ASSERT(this->pimpl->m_initialised); + if (this->pimpl->m_smartBuddyLabel) { + return this->pimpl->m_smartBuddyLabel->getForcedSystemOfMeasurement(); + } else if (this->pimpl->m_fixedDisplayUnit) { + return this->pimpl->m_fixedDisplayUnit->getUnitSystem().systemOfMeasurement; + } + return SmartAmounts::getForcedSystemOfMeasurement(this->pimpl->m_editorName, this->pimpl->m_fieldName); +} + +std::optional SmartField::getForcedRelativeScale() const { + Q_ASSERT(this->pimpl->m_initialised); + if (this->pimpl->m_smartBuddyLabel) { + return this->pimpl->m_smartBuddyLabel->getForcedRelativeScale(); + } else if (this->pimpl->m_fixedDisplayUnit) { + // + // NB: Not every Unit has a RelativeScale. + // For the moment, I'm assuming there are no cases where RelativeScale matters when we have fixed units. If we + // find a case where this is not true, then we'd need to extend UnitSystem to allow it to give us a + // std::optional for a specified Unit in that UnitSystem. + // + return std::nullopt; + } + return SmartAmounts::getForcedRelativeScale(this->pimpl->m_editorName, this->pimpl->m_fieldName); +} + +SmartAmounts::ScaleInfo SmartField::getScaleInfo() const { + Q_ASSERT(this->pimpl->m_initialised); + if (this->pimpl->m_smartBuddyLabel) { + return this->pimpl->m_smartBuddyLabel->getScaleInfo(); + } else if (this->pimpl->m_fixedDisplayUnit) { + return SmartAmounts::ScaleInfo{this->pimpl->m_fixedDisplayUnit->getUnitSystem().systemOfMeasurement, std::nullopt}; + } + + Q_ASSERT(!std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)); + return SmartAmounts::getScaleInfo(this->pimpl->m_editorName, + this->pimpl->m_fieldName, + ConvertToPhysicalQuantities(*this->pimpl->m_typeInfo->fieldType)); +} + +template +void SmartField::setAmount(std::optional amount) { + Q_ASSERT(this->pimpl->m_initialised); + + if (this->pimpl->m_typeInfo->typeIndex != typeid(T)) { + qCritical() << + Q_FUNC_INFO << this->pimpl->m_fieldFqName << ": Trying to set wrong type; m_typeInfo=" << + this->pimpl->m_typeInfo; + } + + if (!amount) { + this->setRawText(""); + return; + } + + this->setAmount(*amount); + return; +} + +// +// Instantiate the above template function 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.) +// +template void SmartField::setAmount(std::optional amount); +template void SmartField::setAmount(std::optional amount); +template void SmartField::setAmount(std::optional amount); + +template void SmartField::setAmount(T amount) { + Q_ASSERT(this->pimpl->m_initialised); + qDebug() << Q_FUNC_INFO << this->pimpl->m_fieldFqName << "amount =" << amount; + + if (this->pimpl->m_typeInfo->typeIndex != typeid(T)) { + qCritical() << + Q_FUNC_INFO << this->pimpl->m_fieldFqName << ": Trying to set wrong type; m_typeInfo=" << this->pimpl->m_typeInfo; + } + + if (std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)) { + // The field is not measuring a physical quantity so there are no units or unit conversions to handle + + NonPhysicalQuantity const nonPhysicalQuantity = + std::get(*this->pimpl->m_typeInfo->fieldType); + // It's a coding error if we're trying to pass a number in to a string field + Q_ASSERT(nonPhysicalQuantity != NonPhysicalQuantity::String); + + // For percentages, we'd like to show the % symbol after the number + QString symbol{""}; + if (NonPhysicalQuantity::Percentage == nonPhysicalQuantity) { + symbol = " %"; + } + + this->setRawText( + Measurement::displayQuantity(amount, this->pimpl->m_precision) + symbol + ); + } else { + // The field is measuring a physical quantity + qDebug() << + Q_FUNC_INFO << this->pimpl->m_fieldFqName << "forcedSystemOfMeasurement:" << + this->getForcedSystemOfMeasurement() << ", forcedRelativeScale:" << + this->getForcedRelativeScale(); + this->setRawText(this->displayAmount(amount)); + } + + return; +} + +template void SmartField::setAmount(int amount); +template void SmartField::setAmount(unsigned int amount); +template void SmartField::setAmount(double amount); + +template T SmartField::getValueAs() const { + qDebug() << + Q_FUNC_INFO << this->pimpl->m_fieldFqName << ": Converting" << this->getRawText() << "to" << + Measurement::extractRawFromString(this->getRawText()); + return Measurement::extractRawFromString(this->getRawText()); +} +// +// Instantiate the above template function 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.) +// +template int SmartField::getValueAs() const; +template unsigned int SmartField::getValueAs() const; +template double SmartField::getValueAs() const; + + +Measurement::PhysicalQuantity SmartField::getPhysicalQuantity() const { + // It's a coding error to call this for NonPhysicalQuantity + Q_ASSERT(!std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)); + + return *this->pimpl->m_currentPhysicalQuantity; +} + +void SmartField::selectPhysicalQuantity(Measurement::PhysicalQuantity const physicalQuantity) { + // It's a coding error to call this for NonPhysicalQuantity + Q_ASSERT(!std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)); + + // It's a coding error to call this if we only hold one PhysicalQuantity + Q_ASSERT(!std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)); + + // It's a coding error to try to select a PhysicalQuantity that was not specified in the constructor + auto const & tupleOfPqs = std::get(*this->pimpl->m_typeInfo->fieldType); + Q_ASSERT(std::get<0>(tupleOfPqs) == physicalQuantity || + std::get<1>(tupleOfPqs) == physicalQuantity); + + this->pimpl->m_currentPhysicalQuantity = physicalQuantity; + return; +} + +///void SmartField::setForcedSystemOfMeasurement(std::optional systemOfMeasurement) { +/// qDebug() << +/// Q_FUNC_INFO << "Measurement system" << systemOfMeasurement << "for" << this->pimpl->m_configSection << ">" << +/// this->pimpl->m_editField; +/// Measurement::setForcedSystemOfMeasurementForField(this->pimpl->m_editField, +/// this->pimpl->m_configSection, +/// systemOfMeasurement); +/// return; +///} +/// +///std::optional SmartField::getForcedSystemOfMeasurement() const { +/// return Measurement::getForcedSystemOfMeasurementForField(this->pimpl->m_editField, this->pimpl->m_configSection); +///} +/// +///void SmartField::setForcedRelativeScale(std::optional relativeScale) { +/// qDebug() << +/// Q_FUNC_INFO << "Scale" << relativeScale << "for" << this->pimpl->m_configSection << ">" << +/// this->pimpl->m_editField; +/// Measurement::setForcedRelativeScaleForField(this->pimpl->m_editField, this->pimpl->m_configSection, relativeScale); +/// return; +///} +/// +///std::optional SmartField::getForcedRelativeScale() const { +/// return Measurement::getForcedRelativeScaleForField(this->pimpl->m_editField, this->pimpl->m_configSection); +///} +/// +///SmartAmounts::ScaleInfo SmartField::getPreviousScaleInfo() const { +/// qDebug() << +/// Q_FUNC_INFO << "Edit Field / Property Name:" << this->pimpl->m_editField << ", Config Section" << +/// this->pimpl->m_configSection; +/// +/// +/// SmartAmounts::ScaleInfo previousScaleInfo{ +/// Measurement::getSystemOfMeasurementForField(this->pimpl->m_editField, this->pimpl->m_configSection, this->pimpl->m_currentPhysicalQuantity), +/// Measurement::getForcedRelativeScaleForField(this->pimpl->m_editField, this->pimpl->m_configSection) +/// }; +/// return previousScaleInfo; +///} + +///Measurement::Amount SmartField::rawToCanonical(QString const & rawValue) const { +/// return Measurement::qStringToSI(rawValue, +/// this->pimpl->m_currentPhysicalQuantity, +/// this->getForcedSystemOfMeasurement(), +/// this->getForcedRelativeScale()); +///} + +QString SmartField::displayAmount(double amount) const { + // It's a coding error to call this for NonPhysicalQuantity + Q_ASSERT(!std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)); + + // I find this a nice level of abstraction. This lets all of the setText() + // methods make a single call w/o having to do the logic for finding the + // unit and scale. + return Measurement::displayAmount( + Measurement::Amount{amount, Measurement::Unit::getCanonicalUnit(*this->pimpl->m_currentPhysicalQuantity)}, + this->pimpl->m_precision, + this->getForcedSystemOfMeasurement(), + this->getForcedRelativeScale() + ); +} + +void SmartField::correctEnteredText(SmartAmounts::ScaleInfo previousScaleInfo) { + Q_ASSERT(this->pimpl->m_initialised); + + // It's a coding error to call this version of correctEnteredText with a NonPhysicalQuantity + Q_ASSERT(!std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)); + + QString const enteredText = this->getRawText(); + + qDebug() << Q_FUNC_INFO << "enteredText:" << enteredText; + + if (enteredText.isEmpty()) { + return; + } + + // The idea here is we need to first translate the field into a known + // amount (aka to SI) and then into the unit we want. + Measurement::Amount amountAsCanonical = this->pimpl->toCanonical(enteredText, previousScaleInfo); + + QString const correctedText = this->displayAmount(amountAsCanonical.quantity()); + qDebug() << + Q_FUNC_INFO << this->getFqFieldName() << "Interpreted" << enteredText << "as" << amountAsCanonical << + "and corrected to" << correctedText; + + this->setRawText(correctedText); + return; +} + +void SmartField::correctEnteredText() { + Q_ASSERT(this->pimpl->m_initialised); + + // It's a coding error to call this version of correctEnteredText with anything other than NonPhysicalQuantity + Q_ASSERT(std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)); + + // .:TBD:. At the moment, the special handling here for types other than double is a bit moot, but we keep it in + // case we need to do more in future. + NonPhysicalQuantity const nonPhysicalQuantity = + std::get(*this->pimpl->m_typeInfo->fieldType); + if (nonPhysicalQuantity != NonPhysicalQuantity::String) { + bool ok = false; + if (this->pimpl->m_typeInfo->typeIndex == typeid(double)) { + double amount = Measurement::extractRawFromString(this->getRawText(), &ok); + this->setAmount(amount); + } else if (this->pimpl->m_typeInfo->typeIndex == typeid(int)) { + int amount = Measurement::extractRawFromString(this->getRawText(), &ok); + this->setAmount(amount); + } else if (this->pimpl->m_typeInfo->typeIndex == typeid(unsigned int)) { + unsigned int amount = Measurement::extractRawFromString(this->getRawText(), &ok); + this->setAmount(amount); + } else { + // It's a coding error if we get here + qCritical() << + Q_FUNC_INFO << this->getFqFieldName() << ": Don't know how to parse" << this->pimpl->m_typeInfo; + Q_ASSERT(false); + } + if (!ok) { + qWarning() << + Q_FUNC_INFO << this->getFqFieldName() << ": Unable to extract number from" << this->getRawText() << + "for" << this->pimpl->m_typeInfo; + this->setAmount(0); + } + } + + return; +} + +///void SmartField::textOrUnitsChanged(SmartAmounts::ScaleInfo previousScaleInfo) { +/// // This is where it gets hard +/// // +/// // We may need to fix the text that the user entered, eg if this field is set to show US Customary volumes and user +/// // enters an amount in litres then we need to convert it to display in pints or quarts etc. +/// QString correctedText; +/// +/// QString rawValue = this->getWidgetRawText(); +/// qDebug() << Q_FUNC_INFO << "rawValue:" << rawValue; +/// +/// if (rawValue.isEmpty()) { +/// return; +/// } +/// +/// // The idea here is we need to first translate the field into a known +/// // amount (aka to SI) and then into the unit we want. +/// Measurement::Amount amountAsCanonical = this->convertToSI(previousScaleInfo); +/// +/// Measurement::PhysicalQuantity physicalQuantity = this->getPhysicalQuantity(); +/// int precision = 3; +/// if (physicalQuantity == Measurement::PhysicalQuantity::Color) { +/// precision = 0; +/// } +/// correctedText = this->displayAmount(amountAsCanonical.quantity(), precision); +/// qDebug() << +/// Q_FUNC_INFO << "Interpreted" << rawValue << "as" << amountAsCanonical << "and corrected to" << correctedText; +/// +/// this->setWidgetRawText(correctedText); +/// return; +///} + +///Measurement::Amount SmartField::convertToSI(SmartAmounts::ScaleInfo previousScaleInfo) { +/// QString rawValue = this->getWidgetRawText(); +/// qDebug() << +/// Q_FUNC_INFO << "rawValue:" << rawValue << ", old SystemOfMeasurement:" << +/// previousScaleInfo.oldSystemOfMeasurement << ", old ForcedScale: " << previousScaleInfo.oldForcedScale; +/// +/// Measurement::UnitSystem const & oldUnitSystem = +/// Measurement::UnitSystem::getInstance(previousScaleInfo.oldSystemOfMeasurement, this->pimpl->m_currentPhysicalQuantity); +/// +/// Measurement::Unit const * defaultUnit{ +/// previousScaleInfo.oldForcedScale ? oldUnitSystem.scaleUnit(*previousScaleInfo.oldForcedScale) : oldUnitSystem.unit() +/// }; +/// +/// // It's a coding error if defaultUnit is null, because it means previousScaleInfo.oldForcedScale was not valid for +/// // oldUnitSystem. However, we can recover. +/// if (!defaultUnit) { +/// qWarning() << Q_FUNC_INFO << "previousScaleInfo.oldForcedScale invalid?" << previousScaleInfo.oldForcedScale; +/// defaultUnit = oldUnitSystem.unit(); +/// } +/// +/// // +/// // Normally, we display units with the text. If the user just edits the number, then the units will still be there. +/// // Alternatively, if the user specifies different units in the text, we should try to honour those. Otherwise, if, +/// // no units are specified in the text, we need to go to defaults. Defaults are either what is "forced" for this +/// // specific field or, failing that, what is configured globally. +/// // +/// // Measurement::UnitSystem::qStringToSI will handle all the logic to deal with any units specified by the user in the +/// // string. (In theory, we just grab the units that the user has specified in the input text. In reality, it's not +/// // that easy as we sometimes need to disambiguate - eg between Imperial gallons and US customary ones. So, if we +/// // have old or current units then that helps with this - eg, if current units are US customary cups and user enters +/// // gallons, then we'll go with US customary gallons over Imperial ones.) +/// // +/// auto amount = oldUnitSystem.qstringToSI(rawValue, *defaultUnit); +/// qDebug() << Q_FUNC_INFO << "Converted to" << amount; +/// return amount; +///} diff --git a/src/SmartField.h b/src/SmartField.h new file mode 100644 index 00000000..d0405410 --- /dev/null +++ b/src/SmartField.h @@ -0,0 +1,279 @@ +/*====================================================================================================================== + * SmartField.h is part of Brewken, and is copyright the following authors 2009-2023: + * • Brian Rower + * • Mark de Wever + * • Matt Young + * • Mike Evans + * • Mik Firestone + * • Philip Greggory Lee + * • Scott Peshak + * + * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see + * . + =====================================================================================================================*/ +#ifndef SMARTFIELD_H +#define SMARTFIELD_H +#pragma once + +#include // For PImpl +#include + +#include + +#include "BtFieldType.h" +#include "measurement/PhysicalQuantity.h" +#include "measurement/Unit.h" +#include "measurement/UnitSystem.h" +#include "utils/BtStringConst.h" +#include "SmartAmounts.h" + +class QWidget; + +class SmartLabel; +class TypeInfo; + +/** + * \class SmartField + * + * \brief Abstract base class that handles all the unit transformation and related logic for displaying a field. Eg, if + * a field holds a volume amount then we need to be able to handle metric / US customary / Imperial systems of + * measurement and to deal with different scales (eg milliliters, liters, etc). Can also deal with a + * \c NonPhysicalQuantity, including making sure that we add a '%' to \c NonPhysicalQuantity::Percentage. + * + * The reason we need this base class, is there are two unrelated classes that need to be able to display amounts + * & units etc: \c SmartField, which allows amounts to be entered and edited, and \c SmartDigitWidget, which + * shows non-editable amounts (typically resulting from a calculation on other fields). + * + * QWidget + * / \ + * ... QLineEdit + * / \ + * / SmartField \ + * QLabel / \ \ + * / \ / \ \ + * SmartLabel SmartDigitWidget SmartField + * + * A number of helper functions exist in the \c SmartAmounts namespace. + * + * Note that although \c SmartLabel and \c SmartDigitWidget both inherit from \c QLabel, they serve different + * purposes. \c SmartLabel tells you what a field is, eg "Target Boil Size" and, where appropriate, allows you + * to select the \c SystemOfMeasurement and/or \c RelativeScale for the field. A \c SmartDigitWidget on the + * other hand shows the value of a field, eg the total amount of NaCl in a water profile. + * + * A \c SmartLabel and a \c SmartField typically work in conjunction with each other, but there are a number of + * edge cases. See comment in \c widgets/SmartLabel.h for more details. + * + * .:TBD:. There is still a certain amount of code duplication between \c SmartLabel and \c SmartField, which it + * would be nice to eliminate somehow. + */ +class SmartField { +public: + SmartField(); + virtual ~SmartField(); + + /** + * \brief This needs to be called before the object is used, typically in constructor of whatever editor is using the + * widget. As well as passing in a bunch of info that cannot easily be given to the constructor (per comment + * above), it also ensures, if necessary, that the \c changedSystemOfMeasurementOrScale signal from the + * \c SmartLabel buddy is connected to the \c lineChanged slot of this \c SmartField. + * + * Note, in reality, you actually use the \c SMART_FIELD_INIT macro (see \c SmartAmounts.h). + * + * \param editorName Name of the owning editor (eg "FermentableEditor"). Together with \c fieldName, should + * uniquely identify this field. + * + * \param fieldName Name of the member variable of this field in its owning editor (eg "field_color"). + * Together with \c editorName, should uniquely identify this field. + * + * \param fieldlFqName This should uniquely identify this field in the application. (Usually, it's a combination + * of the owning widget and the member variable, eg "FermentableEditor->field_color".) + * This is mainly used for logging, where it helps a lot with debugging. (We have hundreds of + * instances of this object and if we detect that one of them is misconfigured, it's very + * useful to be able to log which one!) + * We \b could construct this at run-time from \c editorName and \c fieldName, but, + * since we're being invoked via a macro (\c SMART_FIELD_INIT etc), we might as well have the + * compiler/preprocessor do the necessary concatenation at compile-time and hand the results + * in via this parameter. + * + * \param typeInfo Tells us what data type we use to store the contents of the field (when converted to canonical + * units if it is a \c PhysicalQuantity) and, whether this is an optional field (in which case we + * need to handle blank / empty string as a valid value). + * + * \param buddyLabel Usually needs to be \c QLabel if \c fieldType is a \c NonPhysicalQuantity and \c SmartLabel if + * it is not. However, a \c PhysicalQuantity (or \c Mixed2PhysicalQuantities) field can have a + * \c QLabel (rather than a \c SmartLabel) where the user does \b not have a choice about units or + * scales (even though they otherwise would for this sort of \c PhysicalQuantity). This is + * typically used on conversion dialogs, eg \c RefractoDialog, where we are asking the user to give + * us inputs in specific units in order to convert them to other units measuring the same physical + * quantity. + * + * \param precision For a decimal field, this determines the number of decimal places to show. If not specified, we + * show 3 decimal places. TBD: IDK if one day we might need to be more sophisticated about this, ie + * with number of decimal places dependent on the units that the user has chosen, but for now we + * assume it's the same for everything. + * + * \param maximalDisplayString Used for determining the width of the widget (because a fixed pixel width isn't great + * in a world where there are varying display DPIs). + */ + template + void init(char const * const editorName, + char const * const fieldName, + char const * const fieldlFqName, + LabelType & buddyLabel, + TypeInfo const & typeInfo, + std::optional const precision = std::nullopt, + QString const & maximalDisplayString = "100.000 srm"); + + /** + * \brief Alternate version of \c init for where the display units are fixed, ie not changeable by the user + * (typically for a dialog whose purpose is to convert from one unit to another). + * + * \param buddyLabel Always a \c QLabel + */ + void initFixed(char const * const editorName, + char const * const fieldName, + char const * const fieldlFqName, + QLabel & buddyLabel, + TypeInfo const & typeInfo, + Measurement::Unit const & fixedDisplayUnit, + std::optional const precision = std::nullopt, + QString const & maximalDisplayString = "100.000 srm"); + + /** + * \return \c true if \c init or \c initFixed has been called, \c false otherwise + */ + [[nodiscard]] bool isInitialised() const; + + /** + * \brief Both \c QLabel and \c QLineEdit have a \c text() member function but AFAICT not from the same base class. + * We want to be able to get the text of the widget, so subclasses of \c SmartField need to implement this + * wrapper function that calls \c QLabel::text or \c QLineEdit::text as appropriate. + */ + virtual QString getRawText() const = 0; + + /** + * \brief As with \c getRawText, this is a wrapper function that calls \c QLabel::setText or \c QLineEdit::setText as + * appropriate + */ + virtual void setRawText(QString const & text) = 0; + + /** + * \brief We need our subclasses to handle signal/slot connections for us. (The \c SmartField class cannot inherit + * (directly or indirectly) from \c QObject as then the subclass would be inheriting twice from \c QObject, + * which is (a) bad in general and (b) not supported by the Qt meta-object compiler (MOC). + * + * When we are dealing with a \c PhysicalQuantity, we want to receive the \c changedSystemOfMeasurementOrScale + * signal from \c SmartLabel. + * + * This member function is called from the base class to tell the subclass to make that connection. + */ + virtual void connectSmartLabelSignal(SmartLabel & smartLabel) = 0; + + /** + * \brief Once the base class is initialised, it calls this to allow the derived class to do any of its own + * initialisation. (This is simpler than having the derived class override \c init and \c initFixed.) + */ + virtual void doPostInitWork() = 0; + + BtFieldType const getFieldType() const; + + TypeInfo const & getTypeInfo() const; + + QString const & getMaximalDisplayString() const; + + char const * getFqFieldName() const; + + /** + * \brief If our field type is \b not \c NonPhysicalQuantity, then this returns the field converted to canonical + * units for the relevant \c Measurement::PhysicalQuantity. (It is a coding error to call this function if + * our field type \c is \c NonPhysicalQuantity.) + */ + Measurement::Amount toCanonical() const; + + /** + * \brief Set the amount for a numeric field + * + * \param amount is the amount to display, but the field should be blank if this is \b std::nullopt + */ + template void setAmount(std::optional amount); + template void setAmount(T amount); + + void setForcedSystemOfMeasurement(std::optional systemOfMeasurement); + void setForcedRelativeScale(std::optional relativeScale); + std::optional getForcedSystemOfMeasurement() const; + std::optional getForcedRelativeScale() const; + + /** + * \brief Get the current settings (which may come from system-wide defaults) for \c SystemOfMeasurement and + * \c RelativeScale. + */ + SmartAmounts::ScaleInfo getScaleInfo() const; + + /** + * \brief Use this when you want to get the text as a number (and ignore any units or other trailling letters or + * symbols) + */ + template T getValueAs() const; + + /** + * \brief Returns what type of field this is - except that, if it is \c Mixed2PhysicalQuantities, will one of the two + * possible \c Measurement::PhysicalQuantity values depending on the value of \c this->units. + * + * It is a coding error to call this function if our field type \c is \c NonPhysicalQuantity.) + */ + Measurement::PhysicalQuantity getPhysicalQuantity() const; + + /** + * \brief If the \c Measurement::PhysicalQuantities supplied in the \c init call was not a single + * \c Measurement::PhysicalQuantity, then this member function permits selecting the current + * \c Measurement::PhysicalQuantity from two in the \c Measurement::Mixed2PhysicalQuantities supplied in the + * constructor. + */ + void selectPhysicalQuantity(Measurement::PhysicalQuantity const physicalQuantity); + + /** + * \brief Returns the field converted to canonical units for the relevant \c Measurement::PhysicalQuantity + * + * \param rawValue field text to process + * \return + */ +/// Measurement::Amount rawToCanonical(QString const & rawValue) const; + + /** + * \brief Use this when you want to do something with the returned QString + * + * \param amount Must be in canonical units eg kilograms for mass, liters for volume + */ + [[nodiscard]] QString displayAmount(double amount) const; + + /** + * \brief When the user has finished entering some text, this function does the corrections, eg if the field is set + * to show US Customary volumes and user enters an amount in liters (aka litres) then we need to convert it to + * display in pints or quarts etc. + * + * It is a coding error to call this version of the function when field type is \c NonPhysicalQuantity + * + * \param previousScaleInfo + */ + void correctEnteredText(SmartAmounts::ScaleInfo previousScaleInfo); + + /** + * \brief Version of \c correctEnteredText to call when field type is \c NonPhysicalQuantity + */ + void correctEnteredText(); + +private: + // Private implementation details - see https://herbsutter.com/gotw/_100/ + class impl; + std::unique_ptr pimpl; +}; + +#endif diff --git a/src/StrikeWaterDialog.cpp b/src/StrikeWaterDialog.cpp index a5c92631..c16b20ce 100644 --- a/src/StrikeWaterDialog.cpp +++ b/src/StrikeWaterDialog.cpp @@ -56,6 +56,20 @@ namespace { StrikeWaterDialog::StrikeWaterDialog(QWidget* parent) : QDialog(parent) { setupUi(this); + + // .:TBD:. These label and lineEdit fields could be slightly better named... + SMART_FIELD_INIT_FS(StrikeWaterDialog, grainTempLbl , grainTempVal , double, Measurement::PhysicalQuantity::Temperature); // Initial Infusion: Original Grain Temperature + SMART_FIELD_INIT_FS(StrikeWaterDialog, targetMashLbl , targetMashVal , double, Measurement::PhysicalQuantity::Temperature); // Initial Infusion: Target Mash Temperature + SMART_FIELD_INIT_FS(StrikeWaterDialog, grainWeightInitLbl, grainWeightInitVal, double, Measurement::PhysicalQuantity::Mass ); // Initial Infusion: Weight of Grain + SMART_FIELD_INIT_FS(StrikeWaterDialog, waterVolumeLbl , waterVolumeVal , double, Measurement::PhysicalQuantity::Volume ); // Initial Infusion: Volume of Water + SMART_FIELD_INIT_FS(StrikeWaterDialog, mashVolLbl , mashVolVal , double, Measurement::PhysicalQuantity::Volume ); // Mash Infusion: Total Volume of Water + SMART_FIELD_INIT_FS(StrikeWaterDialog, grainWeightLbl , grainWeightVal , double, Measurement::PhysicalQuantity::Mass ); // Mash Infusion: Grain Weight + SMART_FIELD_INIT_FS(StrikeWaterDialog, actualMashLbl , actualMashVal , double, Measurement::PhysicalQuantity::Temperature); // Mash Infusion: Actual Mash Temperature + SMART_FIELD_INIT_FS(StrikeWaterDialog, targetMashInfLbl , targetMashInfVal , double, Measurement::PhysicalQuantity::Temperature); // Mash Infusion: Target Mash Temperature + SMART_FIELD_INIT_FS(StrikeWaterDialog, infusionWaterLbl , infusionWaterVal , double, Measurement::PhysicalQuantity::Temperature); // Mash Infusion: Infusion Water Temperature + SMART_FIELD_INIT_FS(StrikeWaterDialog, initialResultLbl , initialResultTxt , double, Measurement::PhysicalQuantity::Temperature); // Result: Strike Water Temperature + SMART_FIELD_INIT_FS(StrikeWaterDialog, mashResultLbl , mashResultTxt , double, Measurement::PhysicalQuantity::Volume ); // Result: Volume to add + connect(pushButton_calculate, &QAbstractButton::clicked, this, &StrikeWaterDialog::calculate); return; } @@ -63,18 +77,18 @@ StrikeWaterDialog::StrikeWaterDialog(QWidget* parent) : QDialog(parent) { StrikeWaterDialog::~StrikeWaterDialog() = default; void StrikeWaterDialog::calculate() { - double initial = computeInitialInfusion(); - double mash = computeMashInfusion(); + double strikeWaterTemp = computeInitialInfusion(); + double volumeToAdd = computeMashInfusion(); - this->initialResultTxt->setText(initial); - this->mashResultTxt->setText(mash); + this->initialResultTxt->setAmount(strikeWaterTemp); + this->mashResultTxt ->setAmount(volumeToAdd); return; } double StrikeWaterDialog::computeInitialInfusion() { - double grainTemp = this->grainTempVal->toCanonical().quantity(); - double targetMash = this->targetMashVal->toCanonical().quantity(); - double waterVolume = this->waterVolumeVal->toCanonical().quantity(); + double grainTemp = this->grainTempVal ->toCanonical().quantity(); + double targetMash = this->targetMashVal ->toCanonical().quantity(); + double waterVolume = this->waterVolumeVal ->toCanonical().quantity(); double grainWeight = this->grainWeightInitVal->toCanonical().quantity(); if (grainWeight == 0.0) { @@ -85,9 +99,9 @@ double StrikeWaterDialog::computeInitialInfusion() { } double StrikeWaterDialog::computeMashInfusion() { - double mashVol = this->mashVolVal->toCanonical().quantity(); - double grainWeight = this->grainWeightVal->toCanonical().quantity(); - double actualMash = this->actualMashVal->toCanonical().quantity(); + double mashVol = this->mashVolVal ->toCanonical().quantity(); + double grainWeight = this->grainWeightVal ->toCanonical().quantity(); + double actualMash = this->actualMashVal ->toCanonical().quantity(); double targetMashInf = this->targetMashInfVal->toCanonical().quantity(); double infusionWater = this->infusionWaterVal->toCanonical().quantity(); diff --git a/src/StyleEditor.cpp b/src/StyleEditor.cpp index 2f3411a1..ef948841 100644 --- a/src/StyleEditor.cpp +++ b/src/StyleEditor.cpp @@ -42,38 +42,38 @@ StyleEditor::StyleEditor(QWidget* parent, bool singleStyleEditor) : QDialog{pare this->tabWidget_profile->tabBar()->setStyle(new BtHorizontalTabs); - styleListModel = new StyleListModel(styleComboBox); - styleProxyModel = new StyleSortFilterProxyModel(styleComboBox); - styleProxyModel->setDynamicSortFilter(true); - styleProxyModel->setSourceModel(styleListModel); - styleComboBox->setModel(styleProxyModel); + this->styleListModel = new StyleListModel(styleComboBox); + this->styleProxyModel = new StyleSortFilterProxyModel(styleComboBox); + this->styleProxyModel->setDynamicSortFilter(true); + this->styleProxyModel->setSourceModel(styleListModel); + this->styleComboBox->setModel(styleProxyModel); // Note that the Min / Max pairs of entry fields each share a label (which is shown to the left of both fields) - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_name , PropertyNames::NamedEntity::name ); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_category , PropertyNames::Style::category ); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_categoryNumber, PropertyNames::Style::categoryNumber ); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_styleLetter , PropertyNames::Style::styleLetter ); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_styleGuide , PropertyNames::Style::styleGuide ); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_ogMin , PropertyNames::Style::ogMin , *this->label_og ); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_ogMax , PropertyNames::Style::ogMax , *this->label_og ); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_fgMin , PropertyNames::Style::fgMin , *this->label_fg ); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_fgMax , PropertyNames::Style::fgMax , *this->label_fg ); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_ibuMin , PropertyNames::Style::ibuMin , *this->label_ibu , 0); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_ibuMax , PropertyNames::Style::ibuMax , *this->label_ibu , 0); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_colorMin , PropertyNames::Style::colorMin_srm , *this->label_color ); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_colorMax , PropertyNames::Style::colorMax_srm , *this->label_color ); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_carbMin , PropertyNames::Style::carbMin_vol , *this->label_carb , 0); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_carbMax , PropertyNames::Style::carbMax_vol , *this->label_carb , 0); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_abvMin , PropertyNames::Style::abvMin_pct , 1); - SMART_LINE_EDIT_INIT(StyleEditor, Style, lineEdit_abvMax , PropertyNames::Style::abvMax_pct , 1); + 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); // Note, per https://wiki.qt.io/New_Signal_Slot_Syntax#Default_arguments_in_slot, the use of a trivial lambda // function to allow use of default argument on newStyle() slot - connect(pushButton_save , &QAbstractButton::clicked , this, &StyleEditor::save ); - connect(pushButton_new , &QAbstractButton::clicked , this, [this]() { this->newStyle(); return; } ); - connect(pushButton_cancel, &QAbstractButton::clicked , this, &StyleEditor::clearAndClose ); - connect(pushButton_remove, &QAbstractButton::clicked , this, &StyleEditor::removeStyle ); - connect(styleComboBox , &QComboBox::currentTextChanged, this, &StyleEditor::styleSelected ); + connect(this->pushButton_save , &QAbstractButton::clicked , this, &StyleEditor::save ); + connect(this->pushButton_new , &QAbstractButton::clicked , this, [this]() { this->newStyle(); return; } ); + connect(this->pushButton_cancel, &QAbstractButton::clicked , this, &StyleEditor::clearAndClose ); + connect(this->pushButton_remove, &QAbstractButton::clicked , this, &StyleEditor::removeStyle ); + connect(this->styleComboBox , &QComboBox::currentTextChanged, this, &StyleEditor::styleSelected ); this->setStyle(styleListModel->at(styleComboBox->currentIndex())); return; diff --git a/src/UiAmountWithUnits.cpp b/src/UiAmountWithUnits.cpp deleted file mode 100644 index aa28706c..00000000 --- a/src/UiAmountWithUnits.cpp +++ /dev/null @@ -1,332 +0,0 @@ -/*====================================================================================================================== - * UiAmountWithUnits.cpp is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Mark de Wever - * • Mattias Måhl - * • Matt Young - * • Mike Evans - * • Mik Firestone - * • Philip Greggory Lee - * • Théophane Martin - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "UiAmountWithUnits.h" - -#include - -#include -#include -#include - -#include "Localization.h" -#include "measurement/Measurement.h" -#include "utils/OptionalHelpers.h" - -// This private implementation class holds all private non-virtual members of UiAmountWithUnits -class UiAmountWithUnits::impl { -public: - impl(UiAmountWithUnits & self, - QWidget const * const parent, - Measurement::PhysicalQuantities const physicalQuantities) : - m_self {self}, - m_parent {parent}, - m_allowedPhysicalQuantities{physicalQuantities}, - m_currentPhysicalQuantity { - // If the field supports more than one PhysicalQuantity (eg PqEitherMassOrVolume or - // PqEitherMassOrVolumeConcentration), our starting assumption is that we hold the second one (eg Volume or - // VolumeConcentration). Currently this matters because the assumption is baked into the UI of MiscEditor, - // but we should change that at some point. - std::holds_alternative(physicalQuantities) ? - std::get(physicalQuantities) : - std::get<1>(std::get(physicalQuantities)) - } { - return; - } - - ~impl() = default; - - /** - * \brief Returns the contents of the field converted, if necessary, to SI units - * - * \param enteredText - * \param previousScaleInfo - */ - Measurement::Amount toCanonical(QString const & enteredText, PreviousScaleInfo previousScaleInfo) { - qDebug() << - Q_FUNC_INFO << "enteredText:" << enteredText << ", old SystemOfMeasurement:" << - previousScaleInfo.oldSystemOfMeasurement << ", old ForcedScale: " << previousScaleInfo.oldForcedScale; - - Measurement::UnitSystem const & oldUnitSystem = - Measurement::UnitSystem::getInstance(previousScaleInfo.oldSystemOfMeasurement, - this->m_currentPhysicalQuantity); - - Measurement::Unit const * defaultUnit{ - previousScaleInfo.oldForcedScale ? oldUnitSystem.scaleUnit(*previousScaleInfo.oldForcedScale) : oldUnitSystem.unit() - }; - - // It's a coding error if defaultUnit is null, because it means previousScaleInfo.oldForcedScale was not valid for - // oldUnitSystem. However, we can recover. - if (!defaultUnit) { - qWarning() << Q_FUNC_INFO << "previousScaleInfo.oldForcedScale invalid?" << previousScaleInfo.oldForcedScale; - defaultUnit = oldUnitSystem.unit(); - } - - // - // Normally, we display units with the text. If the user just edits the number, then the units will still be there. - // Alternatively, if the user specifies different units in the text, we should try to honour those. Otherwise, if, - // no units are specified in the text, we need to go to defaults. Defaults are either what is "forced" for this - // specific field or, failing that, what is configured globally. - // - // Measurement::UnitSystem::qStringToSI will handle all the logic to deal with any units specified by the user in the - // string. (In theory, we just grab the units that the user has specified in the input text. In reality, it's not - // that easy as we sometimes need to disambiguate - eg between Imperial gallons and US customary ones. So, if we - // have old or current units then that helps with this - eg, if current units are US customary cups and user enters - // gallons, then we'll go with US customary gallons over Imperial ones.) - // - auto amount = oldUnitSystem.qstringToSI(enteredText, *defaultUnit); - qDebug() << Q_FUNC_INFO << "Converted to" << amount; - return amount; - } - - UiAmountWithUnits & m_self; - QWidget const * const m_parent; - Measurement::PhysicalQuantities const m_allowedPhysicalQuantities; - Measurement::PhysicalQuantity m_currentPhysicalQuantity; - QString m_editField; - QString m_configSection; - -}; - - -UiAmountWithUnits::UiAmountWithUnits(QWidget const * const parent, - Measurement::PhysicalQuantities const physicalQuantities) : - pimpl{std::make_unique(*this, parent, physicalQuantities)} { - return; -} - -UiAmountWithUnits::~UiAmountWithUnits() = default; - -Measurement::PhysicalQuantity UiAmountWithUnits::getPhysicalQuantity() const { - return this->pimpl->m_currentPhysicalQuantity; -} - -void UiAmountWithUnits::selectPhysicalQuantity(Measurement::PhysicalQuantity const physicalQuantity) { - // It's a coding error to call this if we only hold one PhysicalQuantity - Q_ASSERT(!std::holds_alternative(this->pimpl->m_allowedPhysicalQuantities)); - - // It's a coding error to try to select a PhysicalQuantity that was not specified in the constructor - auto const & tupleOfPqs = std::get(this->pimpl->m_allowedPhysicalQuantities); - Q_ASSERT(std::get<0>(tupleOfPqs) == physicalQuantity || - std::get<1>(tupleOfPqs) == physicalQuantity); - - this->pimpl->m_currentPhysicalQuantity = physicalQuantity; - return; -} - -void UiAmountWithUnits::setForcedSystemOfMeasurement(std::optional systemOfMeasurement) { - qDebug() << - Q_FUNC_INFO << "Measurement system" << systemOfMeasurement << "for" << this->pimpl->m_configSection << ">" << - this->pimpl->m_editField; - Measurement::setForcedSystemOfMeasurementForField(this->pimpl->m_editField, - this->pimpl->m_configSection, - systemOfMeasurement); - return; -} - -std::optional UiAmountWithUnits::getForcedSystemOfMeasurement() const { - return Measurement::getForcedSystemOfMeasurementForField(this->pimpl->m_editField, this->pimpl->m_configSection); -} - -void UiAmountWithUnits::setForcedSystemOfMeasurementViaString(QString systemOfMeasurementAsString) { - qDebug() << - Q_FUNC_INFO << "Measurement system" << systemOfMeasurementAsString << "for" << this->pimpl->m_configSection << - ">" << this->pimpl->m_editField; - this->setForcedSystemOfMeasurement(Measurement::getFromUniqueName(systemOfMeasurementAsString)); - return; -} - -QString UiAmountWithUnits::getForcedSystemOfMeasurementViaString() const { - auto forcedSystemOfMeasurement = this->getForcedSystemOfMeasurement(); - return forcedSystemOfMeasurement ? Measurement::getUniqueName(*forcedSystemOfMeasurement) : ""; -} - -void UiAmountWithUnits::setForcedRelativeScale(std::optional relativeScale) { - qDebug() << - Q_FUNC_INFO << "Scale" << relativeScale << "for" << this->pimpl->m_configSection << ">" << - this->pimpl->m_editField; - Measurement::setForcedRelativeScaleForField(this->pimpl->m_editField, this->pimpl->m_configSection, relativeScale); - return; -} - -std::optional UiAmountWithUnits::getForcedRelativeScale() const { - return Measurement::getForcedRelativeScaleForField(this->pimpl->m_editField, this->pimpl->m_configSection); -} - -void UiAmountWithUnits::setForcedRelativeScaleViaString(QString relativeScaleAsString) { - qDebug() << - Q_FUNC_INFO << "Scale" << relativeScaleAsString << "for" << this->pimpl->m_configSection << ">" << - this->pimpl->m_editField; - this->setForcedRelativeScale(Measurement::UnitSystem::getScaleFromUniqueName(relativeScaleAsString)); - return; -} - -QString UiAmountWithUnits::getForcedRelativeScaleViaString() const { - auto forcedRelativeScale = this->getForcedRelativeScale(); - return forcedRelativeScale ? Measurement::UnitSystem::getUniqueName(*forcedRelativeScale) : ""; -} - -void UiAmountWithUnits::setEditField(QString editField) { - this->pimpl->m_editField = editField; - return; -} - -QString UiAmountWithUnits::getEditField() const { - return this->pimpl->m_editField; -} - -void UiAmountWithUnits::setConfigSection(QString configSection) { - // The cascade looks a little odd, but it is intentional. - this->pimpl->m_configSection = configSection; - if (this->pimpl->m_configSection.isEmpty()) { - this->pimpl->m_configSection = - this->pimpl->m_parent->property(*PropertyNames::UiAmountWithUnits::configSection).toString(); - } - if (this->pimpl->m_configSection.isEmpty()) { - this->pimpl->m_configSection = this->pimpl->m_parent->objectName(); - } - return; -} - -QString UiAmountWithUnits::getConfigSection() { - if (this->pimpl->m_configSection.isEmpty()) { - // Setting the config section to blank will actually attempt to populate it with default values -- see - // UiAmountWithUnits::setConfigSection() - this->setConfigSection(""); - } - - return this->pimpl->m_configSection; -} - -///double UiAmountWithUnits::toDoubleRaw(bool * ok) const { -/// return Measurement::extractRawFromString(this->getWidgetText(), ok); -///} - -Measurement::Amount UiAmountWithUnits::rawToCanonical(QString const & rawValue) const { - return Measurement::qStringToSI(rawValue, - this->pimpl->m_currentPhysicalQuantity, - this->getForcedSystemOfMeasurement(), - this->getForcedRelativeScale()); -} - -QString UiAmountWithUnits::displayAmount(double amount, int precision) const { - // I find this a nice level of abstraction. This lets all of the setText() - // methods make a single call w/o having to do the logic for finding the - // unit and scale. - return Measurement::displayAmount( - Measurement::Amount{amount, Measurement::Unit::getCanonicalUnit(this->pimpl->m_currentPhysicalQuantity)}, - precision, - this->getForcedSystemOfMeasurement(), - this->getForcedRelativeScale() - ); -} - -QString UiAmountWithUnits::correctEnteredText(QString const & enteredText, - int precision, - PreviousScaleInfo previousScaleInfo) { - QString correctedText; - - qDebug() << Q_FUNC_INFO << "enteredText:" << enteredText; - - if (enteredText.isEmpty()) { - return enteredText; - } - - // The idea here is we need to first translate the field into a known - // amount (aka to SI) and then into the unit we want. - Measurement::Amount amountAsCanonical = this->pimpl->toCanonical(enteredText, previousScaleInfo); - - correctedText = this->displayAmount(amountAsCanonical.quantity(), precision); - qDebug() << - Q_FUNC_INFO << "Interpreted" << enteredText << "as" << amountAsCanonical << "and corrected to" << correctedText << - "(Edit Field =" << this->pimpl->m_editField << "Config Section =" << this->pimpl->m_configSection << ")"; - - return correctedText; -} - -///void UiAmountWithUnits::textOrUnitsChanged(PreviousScaleInfo previousScaleInfo) { -/// // This is where it gets hard -/// // -/// // We may need to fix the text that the user entered, eg if this field is set to show US Customary volumes and user -/// // enters an amount in litres then we need to convert it to display in pints or quarts etc. -/// QString correctedText; -/// -/// QString rawValue = this->getWidgetText(); -/// qDebug() << Q_FUNC_INFO << "rawValue:" << rawValue; -/// -/// if (rawValue.isEmpty()) { -/// return; -/// } -/// -/// // The idea here is we need to first translate the field into a known -/// // amount (aka to SI) and then into the unit we want. -/// Measurement::Amount amountAsCanonical = this->convertToSI(previousScaleInfo); -/// -/// Measurement::PhysicalQuantity physicalQuantity = this->getPhysicalQuantity(); -/// int precision = 3; -/// if (physicalQuantity == Measurement::PhysicalQuantity::Color) { -/// precision = 0; -/// } -/// correctedText = this->displayAmount(amountAsCanonical.quantity(), precision); -/// qDebug() << -/// Q_FUNC_INFO << "Interpreted" << rawValue << "as" << amountAsCanonical << "and corrected to" << correctedText; -/// -/// this->setWidgetText(correctedText); -/// return; -///} - -///Measurement::Amount UiAmountWithUnits::convertToSI(PreviousScaleInfo previousScaleInfo) { -/// QString rawValue = this->getWidgetText(); -/// qDebug() << -/// Q_FUNC_INFO << "rawValue:" << rawValue << ", old SystemOfMeasurement:" << -/// previousScaleInfo.oldSystemOfMeasurement << ", old ForcedScale: " << previousScaleInfo.oldForcedScale; -/// -/// Measurement::UnitSystem const & oldUnitSystem = -/// Measurement::UnitSystem::getInstance(previousScaleInfo.oldSystemOfMeasurement, this->pimpl->m_currentPhysicalQuantity); -/// -/// Measurement::Unit const * defaultUnit{ -/// previousScaleInfo.oldForcedScale ? oldUnitSystem.scaleUnit(*previousScaleInfo.oldForcedScale) : oldUnitSystem.unit() -/// }; -/// -/// // It's a coding error if defaultUnit is null, because it means previousScaleInfo.oldForcedScale was not valid for -/// // oldUnitSystem. However, we can recover. -/// if (!defaultUnit) { -/// qWarning() << Q_FUNC_INFO << "previousScaleInfo.oldForcedScale invalid?" << previousScaleInfo.oldForcedScale; -/// defaultUnit = oldUnitSystem.unit(); -/// } -/// -/// // -/// // Normally, we display units with the text. If the user just edits the number, then the units will still be there. -/// // Alternatively, if the user specifies different units in the text, we should try to honour those. Otherwise, if, -/// // no units are specified in the text, we need to go to defaults. Defaults are either what is "forced" for this -/// // specific field or, failing that, what is configured globally. -/// // -/// // Measurement::UnitSystem::qStringToSI will handle all the logic to deal with any units specified by the user in the -/// // string. (In theory, we just grab the units that the user has specified in the input text. In reality, it's not -/// // that easy as we sometimes need to disambiguate - eg between Imperial gallons and US customary ones. So, if we -/// // have old or current units then that helps with this - eg, if current units are US customary cups and user enters -/// // gallons, then we'll go with US customary gallons over Imperial ones.) -/// // -/// auto amount = oldUnitSystem.qstringToSI(rawValue, *defaultUnit); -/// qDebug() << Q_FUNC_INFO << "Converted to" << amount; -/// return amount; -///} diff --git a/src/UiAmountWithUnits.h b/src/UiAmountWithUnits.h deleted file mode 100644 index 3c920d09..00000000 --- a/src/UiAmountWithUnits.h +++ /dev/null @@ -1,185 +0,0 @@ -/*====================================================================================================================== - * UiAmountWithUnits.h is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Mark de Wever - * • Matt Young - * • Mike Evans - * • Mik Firestone - * • Philip Greggory Lee - * • Scott Peshak - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#ifndef UIAMOUNTWITHUNITS_H -#define UIAMOUNTWITHUNITS_H -#pragma once - -#include // For PImpl -#include - -#include - -#include "measurement/PhysicalQuantity.h" -#include "measurement/Unit.h" -#include "measurement/UnitSystem.h" -#include "utils/BtStringConst.h" - -//====================================================================================================================== -//========================================== Start of property name constants ========================================== -#define AddPropertyName(property) namespace PropertyNames::UiAmountWithUnits { BtStringConst const property{#property}; } -AddPropertyName(configSection) -#undef AddPropertyName -//=========================================== End of property name constants =========================================== -//====================================================================================================================== - -class QWidget; - -struct PreviousScaleInfo { - Measurement::SystemOfMeasurement oldSystemOfMeasurement; - std::optional oldForcedScale = std::nullopt; -}; - -/** - * \class UiAmountWithUnits A class, suitable for combining with \c QLabel, \c QLineEdit, etc, that handles all the unit - * transformation such a widget would need to do. It is inherited by \c BtDigitWidget and - * \c BtAmountEdit. - */ -class UiAmountWithUnits { -public: - /** - * \param parent The \c QWidget that "owns" us. Used for looking up config section names for retrieving forced - * scales etc for this individual field - * \param physicalQuantities the \c PhysicalQuantity or \c Mixed2PhysicalQuantities to which this amount relates - */ - UiAmountWithUnits(QWidget const * const parent, - Measurement::PhysicalQuantities const physicalQuantities); - virtual ~UiAmountWithUnits(); - - /** - * \brief Returns what type of field this is - except that, if it is \c Mixed2PhysicalQuantities, will one of the two - * possible \c Measurement::PhysicalQuantity values depending on the value of \c this->units. - */ - Measurement::PhysicalQuantity getPhysicalQuantity() const; - - /** - * \brief If the \c Measurement::PhysicalQuantities supplied in the constructor was not a single - * \c Measurement::PhysicalQuantity, then this member function permits selecting the current - * \c Measurement::PhysicalQuantity from two in the \c Measurement::Mixed2PhysicalQuantities supplied in the - * constructor. - */ - void selectPhysicalQuantity(Measurement::PhysicalQuantity const physicalQuantity); - - void setForcedSystemOfMeasurement(std::optional systemOfMeasurement); - void setForcedRelativeScale(std::optional relativeScale); - - std::optional getForcedSystemOfMeasurement() const; - std::optional getForcedRelativeScale() const; - - /** - * \brief QString version of \c setForcedSystemOfMeasurement to work with code generated from .ui files (via - * Q_PROPERTY declared in subclass of this class) - */ - void setForcedSystemOfMeasurementViaString(QString systemOfMeasurementAsString); - - /** - * \brief QString version of \c getForcedSystemOfMeasurement to work with code generated from .ui files (via - * Q_PROPERTY declared in subclass of this class) - */ - QString getForcedSystemOfMeasurementViaString() const; - - /** - * \brief QString version of \c setForcedRelativeScale to work with code generated from .ui files (via Q_PROPERTY - * declared in subclass of this class) - */ - void setForcedRelativeScaleViaString(QString relativeScaleAsString); - - /** - * \brief QString version of \c getForcedRelativeScale to work with code generated from .ui files (via Q_PROPERTY - * declared in subclass of this class) - */ - QString getForcedRelativeScaleViaString() const; - - // By defining the setters/getters, we can remove the need for initializeProperties. - void setEditField(QString editField); - QString getEditField() const; - - void setConfigSection(QString configSection); - QString getConfigSection(); - -/// /** -/// * \brief Converts the numeric part of the input field to a double, ignoring any string suffix. So "5.5 gal" will -/// * give 5.5, "20L" will return 20.0, and so on. -/// */ -/// double toDoubleRaw(bool * ok = nullptr) const; - - /** - * \brief Returns the field converted to canonical units for the relevant \c Measurement::PhysicalQuantity - * - * \param rawValue field text to process - * \return - */ - Measurement::Amount rawToCanonical(QString const & rawValue) const; - - /** - * \brief Use this when you want to do something with the returned QString - * - * \param amount Must be in canonical units eg kilograms for mass, liters for volume - * \param precision Number of decimals to show .:TBD:. Remove default value here? - */ - [[nodiscard]] QString displayAmount(double amount, int precision = 3) const; - - /** - * \brief When the user has finished entering some text, this function does the corrections, eg if the field is set - * to show US Customary volumes and user enters an amount in liters (aka litres) then we need to convert it to - * display in pints or quarts etc. - * \param enteredText Typically retrieved by caller from \c QLabel::text() or \c QLineEdit::text() - * \param precision Number of decimals to show - * \param previousScaleInfo - * - * \return Corrected text that caller should typically pass back to \c QLabel::setText() or \c QLineEdit::setText() - */ - [[nodiscard]] QString correctEnteredText(QString const & enteredText, - int precision, - PreviousScaleInfo previousScaleInfo); - -protected: -/// /** -/// * \brief -/// */ -/// void textOrUnitsChanged(PreviousScaleInfo previousScaleInfo); -private: -/// /** -/// * \brief Returns the contents of the field converted, if necessary, to SI units -/// * -/// * \param oldSystemOfMeasurement -/// * \param oldScale (optional) -/// */ -/// Measurement::Amount convertToSI(PreviousScaleInfo previousScaleInfo); - -protected: - /** - * \brief If \c physicalQuantities is a \c Measurement::PhysicalQuantity, this is the \c Measurement::Unit that - * should be used to store the amount of this field. This is normally fixed as our "standard" (normally - * metric) unit for the \c Measurement::PhysicalQuantity of the field -- eg kilograms for Mass, liters for - * Volume, celsius for Temperature, minutes for Time, etc. However, for \c physicalQuantities of - * \c Mixed2PhysicalQuantities, this will need to vary between two different \c Measurement::Units values - * depending on which \c Measurement::PhysicalQuantity the field is currently set to measure. - */ - Measurement::Unit const * m_canonicalUnits; - -private: - // Private implementation details - see https://herbsutter.com/gotw/_100/ - class impl; - std::unique_ptr pimpl; -}; - -#endif diff --git a/src/WaterDialog.cpp b/src/WaterDialog.cpp index b0d4d8be..36a170f0 100644 --- a/src/WaterDialog.cpp +++ b/src/WaterDialog.cpp @@ -39,7 +39,7 @@ #include "WaterEditor.h" #include "WaterListModel.h" #include "WaterSortFilterProxyModel.h" -#include "widgets/BtDigitWidget.h" +#include "widgets/SmartDigitWidget.h" // @@ -69,8 +69,8 @@ namespace { WaterDialog::WaterDialog(QWidget* parent) : QDialog{parent}, - m_ppm_digits{ QVector{static_cast(Water::Ions::numIons)} }, - m_total_digits{QVector{static_cast(Salt::Types::numTypes)} }, + m_ppm_digits{ QVector{static_cast(Water::Ions::numIons)} }, + m_total_digits{QVector{static_cast(Salt::Types::numTypes)} }, m_rec{nullptr}, m_base{nullptr}, m_target{nullptr}, @@ -115,7 +115,7 @@ WaterDialog::WaterDialog(QWidget* parent) : m_total_digits[static_cast(Salt::Types::NACL )] = btDigit_totalnacl; m_total_digits[static_cast(Salt::Types::NAHCO3)] = btDigit_totalnahco3; - // foreach( BtDigitWidget* i, m_ppm_digits ) { + // foreach( SmartDigitWidget* i, m_ppm_digits ) { for (int i = 0; i < static_cast(Water::Ions::numIons); ++i ) { m_ppm_digits[i]->setLimits(0.0,1000.0); m_ppm_digits[i]->setText(0.0, 1); @@ -128,7 +128,7 @@ WaterDialog::WaterDialog(QWidget* parent) : // since all the things are now digits, lets get the totals configured for (int i = static_cast(Salt::Types::CACL2); i < static_cast(Salt::Types::NAHCO3); ++i ) { - m_total_digits[i]->setConstantColor(BtDigitWidget::BLACK); + m_total_digits[i]->setConstantColor(SmartDigitWidget::BLACK); m_total_digits[i]->setText(0.0,1); } // and now let's see what the table does. @@ -140,6 +140,13 @@ WaterDialog::WaterDialog(QWidget* parent) : m_base_editor = new WaterEditor(this, "Base"); m_target_editor = new WaterEditor(this, "Target"); + SMART_FIELD_INIT_FS(WaterDialog, label_totalcacl2 , btDigit_totalcacl2 , double, Measurement::PhysicalQuantity::Mass, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_totalcaco3 , btDigit_totalcaco3 , double, Measurement::PhysicalQuantity::Mass, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_totalcaso4 , btDigit_totalcaso4 , double, Measurement::PhysicalQuantity::Mass, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_totalmgso4 , btDigit_totalmgso4 , double, Measurement::PhysicalQuantity::Mass, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_totalnacl , btDigit_totalnacl , double, Measurement::PhysicalQuantity::Mass, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_totalnahco3, btDigit_totalnahco3, double, Measurement::PhysicalQuantity::Mass, 2); + // all the signals connect(baseProfileCombo, QOverload::of(&QComboBox::activated), this, &WaterDialog::update_baseProfile ); connect(targetProfileCombo, QOverload::of(&QComboBox::activated), this, &WaterDialog::update_targetProfile); diff --git a/src/WaterDialog.h b/src/WaterDialog.h index 4de647d4..f8771f7f 100644 --- a/src/WaterDialog.h +++ b/src/WaterDialog.h @@ -81,8 +81,8 @@ public slots: double calculateAddedSaltpH(); double calculateAcidpH(); - QVector m_ppm_digits; - QVector m_total_digits; + QVector m_ppm_digits; + QVector m_total_digits; WaterListModel * m_base_combo_list; WaterListModel * m_target_combo_list; SaltTableModel * m_salt_table_model; diff --git a/src/WaterEditor.cpp b/src/WaterEditor.cpp index 04b58ef8..13b7d18e 100644 --- a/src/WaterEditor.cpp +++ b/src/WaterEditor.cpp @@ -57,20 +57,28 @@ WaterEditor::WaterEditor(QWidget *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 , 2); + SMART_FIELD_INIT(WaterEditor, label_pH , lineEdit_ph , Water, PropertyNames::Water::ph , 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->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, &BtLineEdit::textModified, this, &WaterEditor::inputFieldModified); - connect(this->lineEdit_ca, &BtLineEdit::textModified, this, &WaterEditor::inputFieldModified); - connect(this->lineEdit_cl, &BtLineEdit::textModified, this, &WaterEditor::inputFieldModified); - connect(this->lineEdit_mg, &BtLineEdit::textModified, this, &WaterEditor::inputFieldModified); - connect(this->lineEdit_na, &BtLineEdit::textModified, 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, &BtLineEdit::textModified, this, &WaterEditor::inputFieldModified); - connect(this->lineEdit_so4, &BtLineEdit::textModified, 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); this->waterEditRadarChart->init( @@ -176,20 +184,20 @@ void WaterEditor::showChanges(QMetaProperty const * prop) { qDebug() << Q_FUNC_INFO << this->pimpl->editorName << ": Changed" << propName; } - if (updateAll || propName == PropertyNames::NamedEntity::name ) {lineEdit_name->setText(this->pimpl->observedWater->name() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::calcium_ppm ) {lineEdit_ca ->setText(this->pimpl->observedWater->calcium_ppm() , 2); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::magnesium_ppm ) {lineEdit_mg ->setText(this->pimpl->observedWater->magnesium_ppm() , 2); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::sulfate_ppm ) {lineEdit_so4 ->setText(this->pimpl->observedWater->sulfate_ppm() , 2); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::sodium_ppm ) {lineEdit_na ->setText(this->pimpl->observedWater->sodium_ppm() , 2); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::chloride_ppm ) {lineEdit_cl ->setText(this->pimpl->observedWater->chloride_ppm() , 2); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::bicarbonate_ppm ) {lineEdit_alk ->setText(this->pimpl->observedWater->bicarbonate_ppm(), 2); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::ph ) {lineEdit_ph ->setText(this->pimpl->observedWater->ph() , 2); if (!updateAll) { return; } } + 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 ->setAmount(this->pimpl->observedWater->calcium_ppm() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Water::magnesium_ppm ) { this->lineEdit_mg ->setAmount(this->pimpl->observedWater->magnesium_ppm() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Water::sulfate_ppm ) { this->lineEdit_so4 ->setAmount(this->pimpl->observedWater->sulfate_ppm() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Water::sodium_ppm ) { this->lineEdit_na ->setAmount(this->pimpl->observedWater->sodium_ppm() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Water::chloride_ppm ) { this->lineEdit_cl ->setAmount(this->pimpl->observedWater->chloride_ppm() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Water::bicarbonate_ppm ) { this->lineEdit_alk ->setAmount(this->pimpl->observedWater->bicarbonate_ppm()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Water::ph ) { this->lineEdit_ph ->setAmount(this->pimpl->observedWater->ph() ); if (!updateAll) { return; } } if (updateAll || propName == PropertyNames::Water::alkalinityAsHCO3) { bool typeless = this->pimpl->observedWater->alkalinityAsHCO3(); - comboBox_alk->setCurrentIndex(comboBox_alk->findText(typeless ? "HCO3" : "CaCO3")); - if (!updateAll) return; + this->comboBox_alk->setCurrentIndex(comboBox_alk->findText(typeless ? "HCO3" : "CaCO3")); + if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Water::notes ) { plainTextEdit_notes->setPlainText(this->pimpl->observedWater->notes() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Water::notes ) { this->plainTextEdit_notes->setPlainText(this->pimpl->observedWater->notes() ); if (!updateAll) { return; } } return; } diff --git a/src/YeastDialog.cpp b/src/YeastDialog.cpp index 0134916b..c91cf3ca 100644 --- a/src/YeastDialog.cpp +++ b/src/YeastDialog.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * YeastDialog.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * YeastDialog.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Daniel Pettersson * • Matt Young @@ -44,7 +44,7 @@ YeastDialog::YeastDialog(MainWindow* parent) yeastTableProxy->setSourceModel(yeastTableModel); tableWidget->setModel(yeastTableProxy); tableWidget->setSortingEnabled(true); - tableWidget->sortByColumn( YEASTNAMECOL, Qt::AscendingOrder ); + tableWidget->sortByColumn(static_cast(YeastTableModel::ColumnIndex::Name), Qt::AscendingOrder ); yeastTableProxy->setDynamicSortFilter(true); yeastTableProxy->setFilterKeyColumn(1); @@ -167,10 +167,11 @@ void YeastDialog::addYeast(const QModelIndex& index) // Only respond if the name is selected. Since we connect to double-click signal, // this keeps us from adding something to the recipe when we just want to edit // one of the other columns. - if( index.column() == YEASTNAMECOL ) + if (index.column() == static_cast(YeastTableModel::ColumnIndex::Name)) { translated = yeastTableProxy->mapToSource(index); - else + } else { return; + } } // Adds a copy of yeast. diff --git a/src/YeastEditor.cpp b/src/YeastEditor.cpp index 2609fa47..395287eb 100644 --- a/src/YeastEditor.cpp +++ b/src/YeastEditor.cpp @@ -35,12 +35,23 @@ YeastEditor::YeastEditor(QWidget * parent) : obsYeast(nullptr) { setupUi(this); - tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs); + 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::Yeast::amount , 0); + 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_attenuation , lineEdit_attenuation , Yeast, PropertyNames::Yeast::attenuation_pct , 0); + SMART_FIELD_INIT(YeastEditor, label_maxTemperature, lineEdit_maxTemperature, Yeast, PropertyNames::Yeast::maxTemperature_c, 1); + SMART_FIELD_INIT(YeastEditor, label_timesCultured , lineEdit_timesCultured , Yeast, PropertyNames::Yeast::timesCultured , 0); + SMART_FIELD_INIT(YeastEditor, label_maxReuse , lineEdit_maxReuse , Yeast, PropertyNames::Yeast::maxReuse , 0); + // Note, per https://wiki.qt.io/New_Signal_Slot_Syntax#Default_arguments_in_slot, the use of a trivial lambda // function to allow use of default argument on newYeast() slot - connect(pushButton_new, &QAbstractButton::clicked, this, [this]() { this->newYeast(); return; } ); - connect(pushButton_save, &QAbstractButton::clicked, this, &YeastEditor::save ); - connect(pushButton_cancel, &QAbstractButton::clicked, this, &YeastEditor::clearAndClose ); + connect(this->pushButton_new, &QAbstractButton::clicked, this, [this]() { this->newYeast(); return; } ); + connect(this->pushButton_save, &QAbstractButton::clicked, this, &YeastEditor::save ); + connect(this->pushButton_cancel, &QAbstractButton::clicked, this, &YeastEditor::clearAndClose ); return; } @@ -125,21 +136,21 @@ void YeastEditor::showChanges(QMetaProperty * metaProp) { return; } } - if (updateAll || propName == PropertyNames::Yeast::type ) { comboBox_type->setCurrentIndex(static_cast(obsYeast->type())); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::form ) { comboBox_form->setCurrentIndex(static_cast(obsYeast->form())); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::NamedEntityWithInventory::inventory) { lineEdit_inventory ->setText(obsYeast->inventory () , 0); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::amountIsWeight ) { checkBox_amountIsWeight->setCheckState((obsYeast->amountIsWeight()) ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::laboratory ) { lineEdit_laboratory ->setText(obsYeast->laboratory () ); lineEdit_laboratory->setCursorPosition(0); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::productID ) { lineEdit_productID ->setText(obsYeast->productID () ); lineEdit_productID ->setCursorPosition(0); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::minTemperature_c ) { lineEdit_minTemperature->setText(obsYeast->minTemperature_c() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::maxTemperature_c ) { lineEdit_maxTemperature->setText(obsYeast->maxTemperature_c() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::flocculation ) { comboBox_flocculation ->setCurrentIndex(static_cast(obsYeast->flocculation()) ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::attenuation_pct ) { lineEdit_attenuation ->setText(obsYeast->attenuation_pct() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::timesCultured ) { lineEdit_timesCultured ->setText(obsYeast->timesCultured () , 0); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::maxReuse ) { lineEdit_maxReuse ->setText(obsYeast->maxReuse () , 0); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::addToSecondary ) { checkBox_addToSecondary->setCheckState((obsYeast->addToSecondary()) ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::bestFor ) { textEdit_bestFor ->setPlainText(obsYeast->bestFor () ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::notes ) { textEdit_notes ->setPlainText(obsYeast->notes () ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Yeast::type ) { this->comboBox_type->setCurrentIndex(static_cast(obsYeast->type())); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Yeast::form ) { this->comboBox_form->setCurrentIndex(static_cast(obsYeast->form())); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::NamedEntityWithInventory::inventory) { this->lineEdit_inventory ->setAmount (obsYeast->inventory ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Yeast::amountIsWeight ) { this->checkBox_amountIsWeight->setCheckState((obsYeast->amountIsWeight()) ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Yeast::laboratory ) { this->lineEdit_laboratory ->setText (obsYeast->laboratory ()); lineEdit_laboratory->setCursorPosition(0); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Yeast::productID ) { this->lineEdit_productID ->setText (obsYeast->productID ()); lineEdit_productID ->setCursorPosition(0); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Yeast::minTemperature_c ) { this->lineEdit_minTemperature->setAmount (obsYeast->minTemperature_c()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Yeast::maxTemperature_c ) { this->lineEdit_maxTemperature->setAmount (obsYeast->maxTemperature_c()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Yeast::flocculation ) { this->comboBox_flocculation ->setCurrentIndex(static_cast(obsYeast->flocculation())); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Yeast::attenuation_pct ) { this->lineEdit_attenuation ->setAmount (obsYeast->attenuation_pct ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Yeast::timesCultured ) { this->lineEdit_timesCultured ->setAmount (obsYeast->timesCultured ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Yeast::maxReuse ) { this->lineEdit_maxReuse ->setAmount (obsYeast->maxReuse ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Yeast::addToSecondary ) { this->checkBox_addToSecondary->setCheckState((obsYeast->addToSecondary()) ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Yeast::bestFor ) { this->textEdit_bestFor ->setPlainText(obsYeast->bestFor ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Yeast::notes ) { this->textEdit_notes ->setPlainText(obsYeast->notes ()); if (!updateAll) { return; } } return; } diff --git a/src/YeastSortFilterProxyModel.cpp b/src/YeastSortFilterProxyModel.cpp index b06c1571..6e010f10 100644 --- a/src/YeastSortFilterProxyModel.cpp +++ b/src/YeastSortFilterProxyModel.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * YeastSortFilterProxyModel.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * YeastSortFilterProxyModel.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Daniel Pettersson * • Matt Young * • Mik Firestone @@ -38,8 +38,9 @@ bool YeastSortFilterProxyModel::lessThan(const QModelIndex &left, QVariant rightYeast = sourceModel()->data(right); double lAmt, rAmt; - switch (left.column()) { - case YEASTINVENTORYCOL: + auto const columnIndex = static_cast(left.column()); + switch (columnIndex) { + case YeastTableModel::ColumnIndex::Inventory: if (Measurement::qStringToSI(leftYeast.toString(), Measurement::PhysicalQuantity::Volume).quantity() == 0.0 && this->sortOrder() == Qt::AscendingOrder) { return false; @@ -50,10 +51,10 @@ bool YeastSortFilterProxyModel::lessThan(const QModelIndex &left, // This is a lie. I need to figure out if they are weights or volumes. // and then figure some reasonable way to compare weights to volumes. // Maybe lying isn't such a bad idea - case YEASTAMOUNTCOL: + case YeastTableModel::ColumnIndex::Amount: return Measurement::qStringToSI(leftYeast.toString(), Measurement::PhysicalQuantity::Volume) < Measurement::qStringToSI(rightYeast.toString(), Measurement::PhysicalQuantity::Volume); - case YEASTPRODIDCOL: + case YeastTableModel::ColumnIndex::ProdId: lAmt = Localization::toDouble(leftYeast.toString(), Q_FUNC_INFO); rAmt = Localization::toDouble(rightYeast.toString(), Q_FUNC_INFO); return lAmt < rAmt; diff --git a/src/measurement/Amount.cpp b/src/measurement/Amount.cpp index 78ae9f53..ab95196d 100644 --- a/src/measurement/Amount.cpp +++ b/src/measurement/Amount.cpp @@ -22,7 +22,7 @@ namespace Measurement { - Amount::Amount(double quantity, Unit const & unit) : m_quantity{quantity}, m_unit{&unit} { + Amount::Amount(double const quantity, Unit const & unit) : m_quantity{quantity}, m_unit{&unit} { return; } diff --git a/src/measurement/Amount.h b/src/measurement/Amount.h index a6237805..3c7cfee0 100644 --- a/src/measurement/Amount.h +++ b/src/measurement/Amount.h @@ -31,7 +31,7 @@ namespace Measurement { class Amount { public: //! Regular constructor - Amount(double quantity, Unit const & unit); + Amount(double const quantity, Unit const & unit); //! Copy constructor Amount(Amount const & other); diff --git a/src/measurement/Measurement.cpp b/src/measurement/Measurement.cpp index b3433368..a5064711 100644 --- a/src/measurement/Measurement.cpp +++ b/src/measurement/Measurement.cpp @@ -197,42 +197,42 @@ QString Measurement::displayAmount(Measurement::Amount const & amount, return displayUnitSystem.displayAmount(amount, precision, forcedScale); } -QString Measurement::displayAmount(NamedEntity * namedEntity, - QObject * guiObject, - BtStringConst const & propertyName, - Measurement::Unit const & units, - int precision ) { - - if (namedEntity->property(*propertyName).canConvert(QVariant::Double)) { - // Get the amount - QString value = namedEntity->property(*propertyName).toString(); - bool ok = false; - double quantity = Localization::toDouble(value, &ok); - if (!ok) { - qWarning() << Q_FUNC_INFO << "Could not convert " << value << " to double"; - } - - return Measurement::displayAmount( - Measurement::Amount{quantity, units}, - precision, - Measurement::getForcedSystemOfMeasurementForField(*propertyName, guiObject->objectName()), - Measurement::getForcedRelativeScaleForField(*propertyName, guiObject->objectName()) - ); - } - - return "?"; -} - -QString Measurement::displayAmount(Measurement::Amount const & amount, - BtStringConst const & section, - BtStringConst const & propertyName, - int precision) { - return Measurement::displayAmount(amount, - precision, - Measurement::getForcedSystemOfMeasurementForField(*propertyName, *section), - Measurement::getForcedRelativeScaleForField(*propertyName, *section)); - -} +///QString Measurement::displayAmount(NamedEntity * namedEntity, +/// QObject * guiObject, +/// BtStringConst const & propertyName, +/// Measurement::Unit const & units, +/// int precision ) { +/// +/// if (namedEntity->property(*propertyName).canConvert(QVariant::Double)) { +/// // Get the amount +/// QString value = namedEntity->property(*propertyName).toString(); +/// bool ok = false; +/// double quantity = Localization::toDouble(value, &ok); +/// if (!ok) { +/// qWarning() << Q_FUNC_INFO << "Could not convert " << value << " to double"; +/// } +/// +/// return Measurement::displayAmount( +/// Measurement::Amount{quantity, units}, +/// precision, +/// Measurement::getForcedSystemOfMeasurementForField(*propertyName, guiObject->objectName()), +/// Measurement::getForcedRelativeScaleForField(*propertyName, guiObject->objectName()) +/// ); +/// } +/// +/// return "?"; +///} + +///QString Measurement::displayAmount(Measurement::Amount const & amount, +/// BtStringConst const & section, +/// BtStringConst const & propertyName, +/// int precision) { +/// return Measurement::displayAmount(amount, +/// precision, +/// Measurement::getForcedSystemOfMeasurementForField(*propertyName, *section), +/// Measurement::getForcedRelativeScaleForField(*propertyName, *section)); +/// +///} double Measurement::amountDisplay(Measurement::Amount const & amount, std::optional forcedSystemOfMeasurement, @@ -253,67 +253,67 @@ double Measurement::amountDisplay(Measurement::Amount const & amount, return displayUnitSystem.amountDisplay(amount, forcedScale); } -double Measurement::amountDisplay(NamedEntity * namedEntity, - QObject * guiObject, - BtStringConst const & propertyName, - Measurement::Unit const * units) { - - if (namedEntity->property(*propertyName).canConvert(QVariant::Double)) { - // Get the amount - QString value = namedEntity->property(*propertyName).toString(); - bool ok = false; - double amount = Localization::toDouble(value, &ok); - if (!ok) { - qWarning() << Q_FUNC_INFO << "Could not convert" << value << "to double"; - } - - // Special case: we don't know the units of the supplied amount, so just return it as is - if (units == nullptr) { - return amount; - } - - return Measurement::amountDisplay( - Measurement::Amount{amount, *units}, - Measurement::getForcedSystemOfMeasurementForField(*propertyName, guiObject->objectName()), - Measurement::getForcedRelativeScaleForField(*propertyName, guiObject->objectName()) - ); - } - - return -1.0; -} - -QPair Measurement::displayRange(NamedEntity* namedEntity, - QObject *guiObject, - BtStringConst const & propertyNameMin, - BtStringConst const & propertyNameMax, - Unit const * units) { - QPair range; - - if (!namedEntity) { - range.first = 0.0; - range.second = 100.0; - } else { - range.first = amountDisplay(namedEntity, guiObject, propertyNameMin, units); - range.second = amountDisplay(namedEntity, guiObject, propertyNameMax, units); - } - - return range; -} - -QPair Measurement::displayRange(QObject *guiObject, - BtStringConst const & propertyName, - double min, - double max, - Unit const & units) { - auto forcedSystemOfMeasurement = Measurement::getForcedSystemOfMeasurementForField(*propertyName, - guiObject->objectName()); - auto forcedRelativeScale = Measurement::getForcedRelativeScaleForField(*propertyName, guiObject->objectName()); - - QPair range; - range.first = Measurement::amountDisplay(Measurement::Amount{min, units}, forcedSystemOfMeasurement, forcedRelativeScale); - range.second = Measurement::amountDisplay(Measurement::Amount{max, units}, forcedSystemOfMeasurement, forcedRelativeScale); - return range; -} +///double Measurement::amountDisplay(NamedEntity * namedEntity, +/// QObject * guiObject, +/// BtStringConst const & propertyName, +/// Measurement::Unit const * units) { +/// +/// if (namedEntity->property(*propertyName).canConvert(QVariant::Double)) { +/// // Get the amount +/// QString value = namedEntity->property(*propertyName).toString(); +/// bool ok = false; +/// double amount = Localization::toDouble(value, &ok); +/// if (!ok) { +/// qWarning() << Q_FUNC_INFO << "Could not convert" << value << "to double"; +/// } +/// +/// // Special case: we don't know the units of the supplied amount, so just return it as is +/// if (units == nullptr) { +/// return amount; +/// } +/// +/// return Measurement::amountDisplay( +/// Measurement::Amount{amount, *units}, +/// Measurement::getForcedSystemOfMeasurementForField(*propertyName, guiObject->objectName()), +/// Measurement::getForcedRelativeScaleForField(*propertyName, guiObject->objectName()) +/// ); +/// } +/// +/// return -1.0; +///} + +///QPair Measurement::displayRange(NamedEntity* namedEntity, +/// QObject *guiObject, +/// BtStringConst const & propertyNameMin, +/// BtStringConst const & propertyNameMax, +/// Unit const * units) { +/// QPair range; +/// +/// if (!namedEntity) { +/// range.first = 0.0; +/// range.second = 100.0; +/// } else { +/// range.first = amountDisplay(namedEntity, guiObject, propertyNameMin, units); +/// range.second = amountDisplay(namedEntity, guiObject, propertyNameMax, units); +/// } +/// +/// return range; +///} +/// +///QPair Measurement::displayRange(QObject *guiObject, +/// BtStringConst const & propertyName, +/// double min, +/// double max, +/// Unit const & units) { +/// auto forcedSystemOfMeasurement = Measurement::getForcedSystemOfMeasurementForField(*propertyName, +/// guiObject->objectName()); +/// auto forcedRelativeScale = Measurement::getForcedRelativeScaleForField(*propertyName, guiObject->objectName()); +/// +/// QPair range; +/// range.first = Measurement::amountDisplay(Measurement::Amount{min, units}, forcedSystemOfMeasurement, forcedRelativeScale); +/// range.second = Measurement::amountDisplay(Measurement::Amount{max, units}, forcedSystemOfMeasurement, forcedRelativeScale); +/// return range; +///} void Measurement::getThicknessUnits(Unit const ** volumeUnit, Unit const ** weightUnit) { *volumeUnit = Measurement::getDisplayUnitSystem(Measurement::PhysicalQuantity::Volume).thicknessUnit(); @@ -371,97 +371,97 @@ Measurement::Amount Measurement::qStringToSI(QString qstr, return displayUnitSystem.qstringToSI(qstr, *defaultUnit); } -std::optional Measurement::getForcedSystemOfMeasurementForField(QString const & field, - QString const & section) { - if (field.isEmpty()) { - return std::nullopt; - } - return Measurement::getFromUniqueName( - PersistentSettings::value(field, - "None", // This, or any invalid name, will give "no value" return from getFromUniqueName() - section, - PersistentSettings::Extension::UNIT).toString() - ); -} - -std::optional Measurement::getForcedRelativeScaleForField(QString const & field, - QString const & section) { - if (field.isEmpty()) { - return std::nullopt; - } - return Measurement::UnitSystem::getScaleFromUniqueName( - PersistentSettings::value(field, - "None", // This, or any invalid name, will give "no value" return from getFromUniqueName() - section, - PersistentSettings::Extension::SCALE).toString() - ); -} - -void Measurement::setForcedSystemOfMeasurementForField(QString const & field, - QString const & section, - std::optional forcedSystemOfMeasurement) { - if (field.isEmpty()) { - return; - } - if (forcedSystemOfMeasurement) { - PersistentSettings::insert(field, - Measurement::getUniqueName(*forcedSystemOfMeasurement), - section, - PersistentSettings::Extension::UNIT); - } else { - PersistentSettings::remove(field, - section, - PersistentSettings::Extension::UNIT); - } - return; -} - -void Measurement::setForcedRelativeScaleForField(QString const & field, - QString const & section, - std::optional forcedScale) { - if (field.isEmpty()) { - return; - } - if (forcedScale) { - PersistentSettings::insert(field, - Measurement::UnitSystem::getUniqueName(*forcedScale), - section, - PersistentSettings::Extension::SCALE); - } else { - PersistentSettings::remove(field, - section, - PersistentSettings::Extension::SCALE); - } - return; -} - -Measurement::SystemOfMeasurement Measurement::getSystemOfMeasurementForField(QString const & field, - QString const & section, - Measurement::PhysicalQuantities physicalQuantities) { - auto forcedSystemOfMeasurement = Measurement::getForcedSystemOfMeasurementForField(field, section); - if (forcedSystemOfMeasurement) { - return *forcedSystemOfMeasurement; - } - - // If there is no forced System Of Measurement for the field, then we can look to the globally-set UnitSystem for - // this PhysicalQuantity -- except that, if there are two values of PhysicalQuantity, we have to - // choose one arbitrarily. The end result should be the same, because Mass & Volume share the same - // SystemOfMeasurement, as do MassConcentration & VolumeConcentration. - Measurement::PhysicalQuantity const physicalQuantity = - std::holds_alternative(physicalQuantities) ? - std::get(physicalQuantities) : - std::get<0>(std::get(physicalQuantities)); - - return Measurement::getDisplayUnitSystem(physicalQuantity).systemOfMeasurement; -} - - -Measurement::UnitSystem const & Measurement::getUnitSystemForField(QString const & field, - QString const & section, - Measurement::PhysicalQuantity physicalQuantity) { - auto forcedSystemOfMeasurement = Measurement::getForcedSystemOfMeasurementForField(field, section); - if (forcedSystemOfMeasurement) { - return Measurement::UnitSystem::getInstance(*forcedSystemOfMeasurement, physicalQuantity); - } - return Measurement::getDisplayUnitSystem(physicalQuantity); -} +///std::optional Measurement::getForcedSystemOfMeasurementForField(QString const & field, +/// QString const & section) { +/// if (field.isEmpty()) { +/// return std::nullopt; +/// } +/// return Measurement::getFromUniqueName( +/// PersistentSettings::value(field, +/// "None", // This, or any invalid name, will give "no value" return from getFromUniqueName() +/// section, +/// PersistentSettings::Extension::UNIT).toString() +/// ); +///} +/// +///std::optional Measurement::getForcedRelativeScaleForField(QString const & field, +/// QString const & section) { +/// if (field.isEmpty()) { +/// return std::nullopt; +/// } +/// return Measurement::UnitSystem::getScaleFromUniqueName( +/// PersistentSettings::value(field, +/// "None", // This, or any invalid name, will give "no value" return from getFromUniqueName() +/// section, +/// PersistentSettings::Extension::SCALE).toString() +/// ); +///} + +///void Measurement::setForcedSystemOfMeasurementForField(QString const & field, +/// QString const & section, +/// std::optional forcedSystemOfMeasurement) { +/// if (field.isEmpty()) { +/// return; +/// } +/// if (forcedSystemOfMeasurement) { +/// PersistentSettings::insert(field, +/// Measurement::getUniqueName(*forcedSystemOfMeasurement), +/// section, +/// PersistentSettings::Extension::UNIT); +/// } else { +/// PersistentSettings::remove(field, +/// section, +/// PersistentSettings::Extension::UNIT); +/// } +/// return; +///} +/// +///void Measurement::setForcedRelativeScaleForField(QString const & field, +/// QString const & section, +/// std::optional forcedScale) { +/// if (field.isEmpty()) { +/// return; +/// } +/// if (forcedScale) { +/// PersistentSettings::insert(field, +/// Measurement::UnitSystem::getUniqueName(*forcedScale), +/// section, +/// PersistentSettings::Extension::SCALE); +/// } else { +/// PersistentSettings::remove(field, +/// section, +/// PersistentSettings::Extension::SCALE); +/// } +/// return; +///} + +///Measurement::SystemOfMeasurement Measurement::getSystemOfMeasurementForField(QString const & field, +/// QString const & section, +/// Measurement::PhysicalQuantities physicalQuantities) { +/// auto forcedSystemOfMeasurement = Measurement::getForcedSystemOfMeasurementForField(field, section); +/// if (forcedSystemOfMeasurement) { +/// return *forcedSystemOfMeasurement; +/// } +/// +/// // If there is no forced System Of Measurement for the field, then we can look to the globally-set UnitSystem for +/// // this PhysicalQuantity -- except that, if there are two values of PhysicalQuantity, we have to +/// // choose one arbitrarily. The end result should be the same, because Mass & Volume share the same +/// // SystemOfMeasurement, as do MassConcentration & VolumeConcentration. +/// Measurement::PhysicalQuantity const physicalQuantity = +/// std::holds_alternative(physicalQuantities) ? +/// std::get(physicalQuantities) : +/// std::get<0>(std::get(physicalQuantities)); +/// +/// return Measurement::getDisplayUnitSystem(physicalQuantity).systemOfMeasurement; +///} +/// +/// +///Measurement::UnitSystem const & Measurement::getUnitSystemForField(QString const & field, +/// QString const & section, +/// Measurement::PhysicalQuantity physicalQuantity) { +/// auto forcedSystemOfMeasurement = Measurement::getForcedSystemOfMeasurementForField(field, section); +/// if (forcedSystemOfMeasurement) { +/// return Measurement::UnitSystem::getInstance(*forcedSystemOfMeasurement, physicalQuantity); +/// } +/// return Measurement::getDisplayUnitSystem(physicalQuantity); +///} diff --git a/src/measurement/Measurement.h b/src/measurement/Measurement.h index e12ecb09..fcb3000d 100644 --- a/src/measurement/Measurement.h +++ b/src/measurement/Measurement.h @@ -59,13 +59,6 @@ namespace Measurement { */ UnitSystem const & getDisplayUnitSystem(PhysicalQuantity physicalQuantity); - // Previous function Measurement::getUnitForInternalStorage() is replaced by Measurement::Unit::getCanonicalUnit() -// /** -// * \brief Returns the \c Unit (usually a Metric/SI one where this is an option) that we use for storing a given -// * \c PhysicalQuantity -// */ -// Unit const & getUnitForInternalStorage(PhysicalQuantity physicalQuantity); - /*! * \brief Converts a quantity without units to a displayable string * @@ -88,36 +81,36 @@ namespace Measurement { std::optional forcedSystemOfMeasurement = std::nullopt, std::optional forcedScale = std::nullopt); - /*! - * \brief Converts a measurement (aka amount) to a displayable string in the appropriate units. - * - * \param namedEntity Named Entity of which we want to display a property - * \param guiObject the GUI object doing the display, used to access configured unit system & scale - * \param propertyName the \c QObject::property of \c namedEntity that returns the amount we wish to display - * \param units which unit system it is in - * \param precision how many decimal places to use, defaulting to 3 - */ - QString displayAmount(NamedEntity * namedEntity, - QObject * guiObject, - BtStringConst const & propertyName, - Measurement::Unit const & units, - int precision = 3); - - /*! - * \brief Converts a measurement (aka amount) to a displayable string in the appropriate units. - * - * .:TBD:. Need to check we do the right thing for measurements that can be either mass or volume (eg Misc, - * Fermentable, etc) - * - * \param amount the amount to display - * \param section the name of the object to reference to get unit system & scales from the config file - * \param propertyName the property name to complete the lookup for units & scales - * \param precision how many decimal places to use, defaulting to 3 - */ - QString displayAmount(Measurement::Amount const & amount, - BtStringConst const & section, - BtStringConst const & propertyName, - int precision = 3); +/// /*! +/// * \brief Converts a measurement (aka amount) to a displayable string in the appropriate units. +/// * +/// * \param namedEntity Named Entity of which we want to display a property +/// * \param guiObject the GUI object doing the display, used to access configured unit system & scale +/// * \param propertyName the \c QObject::property of \c namedEntity that returns the amount we wish to display +/// * \param units which unit system it is in +/// * \param precision how many decimal places to use, defaulting to 3 +/// */ +/// QString displayAmount(NamedEntity * namedEntity, +/// QObject * guiObject, +/// BtStringConst const & propertyName, +/// Measurement::Unit const & units, +/// int precision = 3); + +/// /*! +/// * \brief Converts a measurement (aka amount) to a displayable string in the appropriate units. +/// * +/// * .:TBD:. Need to check we do the right thing for measurements that can be either mass or volume (eg Misc, +/// * Fermentable, etc) +/// * +/// * \param amount the amount to display +/// * \param section the name of the object to reference to get unit system & scales from the config file +/// * \param propertyName the property name to complete the lookup for units & scales +/// * \param precision how many decimal places to use, defaulting to 3 +/// */ +/// QString displayAmount(Measurement::Amount const & amount, +/// BtStringConst const & section, +/// BtStringConst const & propertyName, +/// int precision = 3); /*! * \brief Converts a measurement (aka amount) to its numerical equivalent in the specified or default units. @@ -131,50 +124,50 @@ namespace Measurement { std::optional forcedSystemOfMeasurement = std::nullopt, std::optional forcedScale = std::nullopt); - /*! - * \brief Converts a measurement (aka amount) to its numerical equivalent in the specified or default units. - * - * \param namedEntity Named Entity of which we want to display a property - * \param guiObject the GUI object doing the display, used to access configured unit system & scale - * \param propertyName the \c QObject::property of \c namedEntity that returns the amount we wish to display - * \param units the units that the measurement (amount) is in - */ - double amountDisplay(NamedEntity* namedEntity, - QObject* guiObject, - BtStringConst const & propertyName, - Unit const * units = nullptr); - - /** - * \brief Converts a range (ie min/max pair) of measurements (aka amounts) to its numerical equivalent in whatever - * units are configured for this property. - * - * \param namedEntity Named Entity of which we want to display a property - * \param guiObject the GUI object doing the display, used to access configured unit system & scale - * \param propertyNameMin - * \param propertyNameMax - * \param units the units that the measurement (amount) is in - */ - QPair displayRange(NamedEntity* namedEntity, - QObject *guiObject, - BtStringConst const & propertyNameMin, - BtStringConst const & propertyNameMax, - Unit const * units = nullptr); - - /** - * \brief Converts a range (ie min/max pair) of measurements (aka amounts) to its numerical equivalent in whatever - * units are configured for this property. - * - * \param guiObject the GUI object doing the display, used to access configured unit system & scale - * \param propertyName - * \param min - * \param max - * \param units the units that the measurement (amount) is in - */ - QPair displayRange(QObject *guiObject, - BtStringConst const & propertyName, - double min, - double max, - Unit const & units); +/// /*! +/// * \brief Converts a measurement (aka amount) to its numerical equivalent in the specified or default units. +/// * +/// * \param namedEntity Named Entity of which we want to display a property +/// * \param guiObject the GUI object doing the display, used to access configured unit system & scale +/// * \param propertyName the \c QObject::property of \c namedEntity that returns the amount we wish to display +/// * \param units the units that the measurement (amount) is in +/// */ +/// double amountDisplay(NamedEntity* namedEntity, +/// QObject* guiObject, +/// BtStringConst const & propertyName, +/// Unit const * units = nullptr); +/// +/// /** +/// * \brief Converts a range (ie min/max pair) of measurements (aka amounts) to its numerical equivalent in whatever +/// * units are configured for this property. +/// * +/// * \param namedEntity Named Entity of which we want to display a property +/// * \param guiObject the GUI object doing the display, used to access configured unit system & scale +/// * \param propertyNameMin +/// * \param propertyNameMax +/// * \param units the units that the measurement (amount) is in +/// */ +/// QPair displayRange(NamedEntity* namedEntity, +/// QObject *guiObject, +/// BtStringConst const & propertyNameMin, +/// BtStringConst const & propertyNameMax, +/// Unit const * units = nullptr); +/// +/// /** +/// * \brief Converts a range (ie min/max pair) of measurements (aka amounts) to its numerical equivalent in whatever +/// * units are configured for this property. +/// * +/// * \param guiObject the GUI object doing the display, used to access configured unit system & scale +/// * \param propertyName +/// * \param min +/// * \param max +/// * \param units the units that the measurement (amount) is in +/// */ +/// QPair displayRange(QObject *guiObject, +/// BtStringConst const & propertyName, +/// double min, +/// double max, +/// Unit const & units); //! \brief Displays thickness in appropriate units from standard thickness in L/kg. QString displayThickness(double thick_lkg, bool showUnits = true); @@ -199,40 +192,36 @@ namespace Measurement { std::optional forcedScale = std::nullopt); - /** - * \brief .:TODO:. Need some additional thought on how \c getForcedSystemOfMeasurementForField and - * \c getForcedRelativeScaleForField should work for fields that can be either mass or volume. - */ - std::optional getForcedSystemOfMeasurementForField(QString const & field, - QString const & section); - std::optional getForcedRelativeScaleForField(QString const & field, - QString const & section); - - void setForcedSystemOfMeasurementForField(QString const & field, - QString const & section, - std::optional forcedSystemOfMeasurement); - - void setForcedRelativeScaleForField(QString const & field, - QString const & section, - std::optional forcedScale); - - /** - * \brief Returns the \c SystemOfMeasurement that should be used to display this field, based on the forced - * \c SystemOfMeasurement for the field if there is one or otherwise on the the system-wide default - * \c UnitSystem for the specified \c PhysicalQuantity. - */ - Measurement::SystemOfMeasurement getSystemOfMeasurementForField(QString const & field, - QString const & section, - Measurement::PhysicalQuantities physicalQuantities); - - /** - * \brief Returns the \c UnitSystem that should be used to display this field, based on the forced - * \c SystemOfMeasurement for the field if there is one or otherwise on the the system-wide default - * \c UnitSystem for the specified \c PhysicalQuantity. - */ - Measurement::UnitSystem const & getUnitSystemForField(QString const & field, - QString const & section, - Measurement::PhysicalQuantity physicalQuantity); +/// /** +/// * \brief .:TODO:. Need some additional thought on how \c getForcedSystemOfMeasurementForField and +/// * \c getForcedRelativeScaleForField should work for fields that can be either mass or volume. +/// */ +/// std::optional getForcedSystemOfMeasurementForField(QString const & field, +/// QString const & section); +/// std::optional getForcedRelativeScaleForField(QString const & field, +/// QString const & section); +/// +/// void setForcedSystemOfMeasurementForField(QString const & field, +/// QString const & section, +/// std::optional forcedSystemOfMeasurement); +/// +/// void setForcedRelativeScaleForField(QString const & field, +/// QString const & section, +/// std::optional forcedScale); +/// +/// /** +/// * \brief Returns the \c SystemOfMeasurement that should be used to display this field, based on the forced +/// * \c SystemOfMeasurement for the field if there is one or otherwise on the the system-wide default +/// * \c UnitSystem for the specified \c PhysicalQuantity. +/// */ +/// Measurement::SystemOfMeasurement getSystemOfMeasurementForField(Measurement::PhysicalQuantities physicalQuantities); +/// +/// /** +/// * \brief Returns the \c UnitSystem that should be used to display this field, based on the forced +/// * \c SystemOfMeasurement for the field if there is one or otherwise on the the system-wide default +/// * \c UnitSystem for the specified \c PhysicalQuantity. +/// */ +/// Measurement::UnitSystem const & getUnitSystemForField(Measurement::PhysicalQuantity physicalQuantity); } #endif diff --git a/src/model/BrewNote.cpp b/src/model/BrewNote.cpp index 31757411..b8f06f85 100644 --- a/src/model/BrewNote.cpp +++ b/src/model/BrewNote.cpp @@ -66,7 +66,7 @@ TypeLookup const BrewNote::typeLookup { { // Note that we need Enums to be treated as ints for the purposes of type lookup PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::abv , BrewNote::m_abv , NonPhysicalQuantity::Percentage ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::attenuation , BrewNote::m_attenuation ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::attenuation , BrewNote::m_attenuation , NonPhysicalQuantity::Percentage ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::boilOff_l , BrewNote::m_boilOff_l , Measurement::PhysicalQuantity::Volume ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::brewDate , BrewNote::m_brewDate , NonPhysicalQuantity::Date ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::brewhouseEff_pct , BrewNote::m_brewhouseEff_pct , NonPhysicalQuantity::Percentage ), @@ -80,7 +80,7 @@ TypeLookup const BrewNote::typeLookup { PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::pitchTemp_c , BrewNote::m_pitchTemp_c , Measurement::PhysicalQuantity::Temperature), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::postBoilVolume_l , BrewNote::m_postBoilVolume_l , Measurement::PhysicalQuantity::Volume ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projABV_pct , BrewNote::m_projABV_pct , NonPhysicalQuantity::Percentage ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projAtten , BrewNote::m_projAtten ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projAtten , BrewNote::m_projAtten , NonPhysicalQuantity::Percentage ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projBoilGrav , BrewNote::m_projBoilGrav , Measurement::PhysicalQuantity::Density ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projEff_pct , BrewNote::m_projEff_pct , NonPhysicalQuantity::Percentage ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::BrewNote::projFermPoints , BrewNote::m_projFermPoints ), diff --git a/src/model/MashStep.cpp b/src/model/MashStep.cpp index 354c0473..0ace2fa6 100644 --- a/src/model/MashStep.cpp +++ b/src/model/MashStep.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * model/MashStep.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * model/MashStep.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Mattias Måhl * • Matt Young @@ -56,15 +56,15 @@ ObjectStore & MashStep::getObjectStoreTypedInstance() const { TypeLookup const MashStep::typeLookup { "MashStep", { - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::MashStep::decoctionAmount_l, MashStep::m_decoctionAmount_l), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::MashStep::endTemp_c , MashStep::m_endTemp_c ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::MashStep::infuseAmount_l , MashStep::m_infuseAmount_l ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::MashStep::infuseTemp_c , MashStep::m_infuseTemp_c ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::MashStep::decoctionAmount_l, MashStep::m_decoctionAmount_l, Measurement::PhysicalQuantity::Volume ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::MashStep::endTemp_c , MashStep::m_endTemp_c , Measurement::PhysicalQuantity::Temperature ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::MashStep::infuseAmount_l , MashStep::m_infuseAmount_l , Measurement::PhysicalQuantity::Volume ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::MashStep::infuseTemp_c , MashStep::m_infuseTemp_c , Measurement::PhysicalQuantity::Temperature ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::MashStep::mashId , MashStep::m_mashId ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::MashStep::rampTime_min , MashStep::m_rampTime_min ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::MashStep::rampTime_min , MashStep::m_rampTime_min , Measurement::PhysicalQuantity::Time ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::MashStep::stepNumber , MashStep::m_stepNumber ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::MashStep::stepTemp_c , MashStep::m_stepTemp_c ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::MashStep::stepTime_min , MashStep::m_stepTime_min ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::MashStep::stepTemp_c , MashStep::m_stepTemp_c , Measurement::PhysicalQuantity::Temperature ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::MashStep::stepTime_min , MashStep::m_stepTime_min , Measurement::PhysicalQuantity::Time ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::MashStep::type , MashStep::m_type ), // PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::MashStep::typeString , MashStep::m_typeString ), }, diff --git a/src/model/Recipe.cpp b/src/model/Recipe.cpp index a0d657b2..a52098cc 100644 --- a/src/model/Recipe.cpp +++ b/src/model/Recipe.cpp @@ -738,36 +738,22 @@ QVector Recipe::mashInstructions(double timeRemaining, QString str; if (step->isInfusion()) { str = tr("Add %1 water at %2 to mash to bring it to %3.") - .arg(Measurement::displayAmount(Measurement::Amount{step->infuseAmount_l(), Measurement::Units::liters}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::infuseAmount_l)) - .arg(Measurement::displayAmount(Measurement::Amount{step->infuseTemp_c(), Measurement::Units::celsius}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::infuseTemp_c)) - .arg(Measurement::displayAmount(Measurement::Amount{step->stepTemp_c(), Measurement::Units::celsius}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::stepTemp_c)); + .arg(Measurement::displayAmount(Measurement::Amount{step->infuseAmount_l(), Measurement::Units::liters})) + .arg(Measurement::displayAmount(Measurement::Amount{step->infuseTemp_c(), Measurement::Units::celsius})) + .arg(Measurement::displayAmount(Measurement::Amount{step->stepTemp_c(), Measurement::Units::celsius})); totalWaterAdded_l += step->infuseAmount_l(); } else if (step->isTemperature()) { str = tr("Heat mash to %1.").arg(Measurement::displayAmount(Measurement::Amount{step->stepTemp_c(), - Measurement::Units::celsius}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::stepTemp_c)); + Measurement::Units::celsius})); } else if (step->isDecoction()) { str = tr("Bring %1 of the mash to a boil and return to the mash tun to bring it to %2.") .arg(Measurement::displayAmount(Measurement::Amount{step->decoctionAmount_l(), - Measurement::Units::liters}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::decoctionAmount_l)) - .arg(Measurement::displayAmount(Measurement::Amount{step->stepTemp_c(), Measurement::Units::celsius}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::stepTemp_c)); + Measurement::Units::liters})) + .arg(Measurement::displayAmount(Measurement::Amount{step->stepTemp_c(), Measurement::Units::celsius})); } str += tr(" Hold for %1.").arg(Measurement::displayAmount(Measurement::Amount{step->stepTime_min(), - Measurement::Units::minutes}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::stepTime_min)); + Measurement::Units::minutes})); preins.push_back(PreInstruction(str, QString("%1 - %2").arg(step->typeStringTr()).arg(step->name()), timeRemaining)); @@ -801,13 +787,9 @@ QVector Recipe::hopSteps(Hop::Use type) { str = tr("Use %1 %2 for %3"); } - str = str.arg(Measurement::displayAmount(Measurement::Amount{hop->amount_kg(), Measurement::Units::kilograms}, - PersistentSettings::Sections::hopTable, - PropertyNames::Hop::amount_kg)) + str = str.arg(Measurement::displayAmount(Measurement::Amount{hop->amount_kg(), Measurement::Units::kilograms})) .arg(hop->name()) - .arg(Measurement::displayAmount(Measurement::Amount{hop->time_min(), Measurement::Units::minutes}, - PersistentSettings::Sections::hopTable, - PropertyNames::Misc::time)); + .arg(Measurement::displayAmount(Measurement::Amount{hop->time_min(), Measurement::Units::minutes})); preins.push_back(PreInstruction(str, tr("Hop addition"), hop->time_min())); } @@ -842,13 +824,9 @@ QVector Recipe::miscSteps(Misc::Use type) { str = str .arg(Measurement::displayAmount(Measurement::Amount{ misc->amount(), misc->amountIsWeight() ? Measurement::Units::kilograms : Measurement::Units::liters - }, - PersistentSettings::Sections::miscTableModel, - PropertyNames::Misc::amount)) + })) .arg(misc->name()) - .arg(Measurement::displayAmount(Measurement::Amount{misc->time(), Measurement::Units::minutes}, - PersistentSettings::Sections::miscTableModel, - PropertyNames::Misc::time)); + .arg(Measurement::displayAmount(Measurement::Amount{misc->time(), Measurement::Units::minutes})); preins.push_back(PreInstruction(str, tr("Misc addition"), misc->time())); } @@ -886,21 +864,15 @@ void Recipe::topOffIns() { double wortInBoil_l = wortFromMash_l() - e->lauterDeadspace_l(); QString str = tr("You should now have %1 wort.") - .arg(Measurement::displayAmount(Measurement::Amount{wortInBoil_l, Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilSize_l)); + .arg(Measurement::displayAmount(Measurement::Amount{wortInBoil_l, Measurement::Units::liters})); if (e->topUpKettle_l() != 0.0) { return; } wortInBoil_l += e->topUpKettle_l(); QString tmp = tr(" Add %1 water to the kettle, bringing pre-boil volume to %2.") - .arg(Measurement::displayAmount(Measurement::Amount{e->topUpKettle_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilSize_l)) - .arg(Measurement::displayAmount(Measurement::Amount{wortInBoil_l, Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilSize_l)); + .arg(Measurement::displayAmount(Measurement::Amount{e->topUpKettle_l(), Measurement::Units::liters})) + .arg(Measurement::displayAmount(Measurement::Amount{wortInBoil_l, Measurement::Units::liters})); str += tmp; @@ -951,9 +923,7 @@ PreInstruction Recipe::boilFermentablesPre(double timeRemaining) { } str += QString("%1 %2, ") - .arg(Measurement::displayAmount(ferm->amountWithUnits(), - PersistentSettings::Sections::fermentableTable, - PropertyNames::Fermentable::amountWithUnits)) + .arg(Measurement::displayAmount(ferm->amountWithUnits())) .arg(ferm->name()); } str += "."; @@ -977,9 +947,7 @@ PreInstruction Recipe::addExtracts(double timeRemaining) const { const Fermentable * ferm = flist[i]; if (ferm->isExtract()) { str += QString("%1 %2, ") - .arg(Measurement::displayAmount(ferm->amountWithUnits(), - PersistentSettings::Sections::fermentableTable, - PropertyNames::Fermentable::amountWithUnits)) + .arg(Measurement::displayAmount(ferm->amountWithUnits())) .arg(ferm->name()); } } @@ -1003,9 +971,7 @@ void Recipe::postboilFermentablesIns() { hasFerms = true; tmp = QString("%1 %2, ") - .arg(Measurement::displayAmount(ferm->amountWithUnits(), - PersistentSettings::Sections::fermentableTable, - PropertyNames::Fermentable::amountWithUnits)) + .arg(Measurement::displayAmount(ferm->amountWithUnits())) .arg(ferm->name()); str += tmp; } @@ -1038,24 +1004,16 @@ void Recipe::postboilIns() { double wort_l = e->wortEndOfBoil_l(wortInBoil_l); QString str = tr("You should have %1 wort post-boil.") - .arg(Measurement::displayAmount(Measurement::Amount{wort_l, Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::batchSize_l)); + .arg(Measurement::displayAmount(Measurement::Amount{wort_l, Measurement::Units::liters})); str += tr("\nYou anticipate losing %1 to trub and chiller loss.") - .arg(Measurement::displayAmount(Measurement::Amount{e->trubChillerLoss_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::batchSize_l)); + .arg(Measurement::displayAmount(Measurement::Amount{e->trubChillerLoss_l(), Measurement::Units::liters})); wort_l -= e->trubChillerLoss_l(); if (e->topUpWater_l() > 0.0) str += tr("\nAdd %1 top up water into primary.") - .arg(Measurement::displayAmount(Measurement::Amount{e->topUpWater_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::batchSize_l)); + .arg(Measurement::displayAmount(Measurement::Amount{e->topUpWater_l(), Measurement::Units::liters})); wort_l += e->topUpWater_l(); str += tr("\nThe final volume in the primary is %1.") - .arg(Measurement::displayAmount(Measurement::Amount{wort_l, Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::batchSize_l)); + .arg(Measurement::displayAmount(Measurement::Amount{wort_l, Measurement::Units::liters})); auto ins = std::make_shared(); ins->setName(tr("Post boil")); @@ -1142,9 +1100,7 @@ void Recipe::generateInstructions() { } QString str = tr("Bring the wort to a boil and hold for %1.").arg( - Measurement::displayAmount(Measurement::Amount{timeRemaining, Measurement::Units::minutes}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilTime_min) + Measurement::displayAmount(Measurement::Amount{timeRemaining, Measurement::Units::minutes}) ); auto startBoilIns = std::make_shared(); @@ -1215,10 +1171,7 @@ void Recipe::generateInstructions() { addPreinstructions(miscSteps(Misc::Use::Primary)); str = tr("Let ferment until FG is %1.").arg( - Measurement::displayAmount(Measurement::Amount{fg(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::fg, - 3) + Measurement::displayAmount(Measurement::Amount{fg(), Measurement::Units::sp_grav}, 3) ); auto fermentIns = std::make_shared(); @@ -1264,13 +1217,9 @@ QString Recipe::nextAddToBoil(double & time) { } if (h->time_min() < time && h->time_min() > max) { ret = tr("Add %1 %2 to boil at %3.") - .arg(Measurement::displayAmount(Measurement::Amount{h->amount_kg(), Measurement::Units::kilograms}, - PersistentSettings::Sections::hopTable, - PropertyNames::Hop::amount_kg)) + .arg(Measurement::displayAmount(Measurement::Amount{h->amount_kg(), Measurement::Units::kilograms})) .arg(h->name()) - .arg(Measurement::displayAmount(Measurement::Amount{h->time_min(), Measurement::Units::minutes}, - PersistentSettings::Sections::hopTable, - PropertyNames::Misc::time)); + .arg(Measurement::displayAmount(Measurement::Amount{h->time_min(), Measurement::Units::minutes})); max = h->time_min(); foundSomething = true; @@ -1287,19 +1236,13 @@ QString Recipe::nextAddToBoil(double & time) { if (m->time() < time && m->time() > max) { ret = tr("Add %1 %2 to boil at %3."); if (m->amountIsWeight()) { - ret = ret.arg(Measurement::displayAmount(Measurement::Amount{m->amount(), Measurement::Units::kilograms}, - PersistentSettings::Sections::miscTableModel, - PropertyNames::Misc::amount)); + ret = ret.arg(Measurement::displayAmount(Measurement::Amount{m->amount(), Measurement::Units::kilograms})); } else { - ret = ret.arg(Measurement::displayAmount(Measurement::Amount{m->amount(), Measurement::Units::liters}, - PersistentSettings::Sections::miscTableModel, - PropertyNames::Misc::amount)); + ret = ret.arg(Measurement::displayAmount(Measurement::Amount{m->amount(), Measurement::Units::liters})); } ret = ret.arg(m->name()); - ret = ret.arg(Measurement::displayAmount(Measurement::Amount{m->time(), Measurement::Units::minutes}, - PersistentSettings::Sections::miscTableModel, - PropertyNames::Misc::time)); + ret = ret.arg(Measurement::displayAmount(Measurement::Amount{m->time(), Measurement::Units::minutes})); max = m->time(); foundSomething = true; } @@ -2749,9 +2692,7 @@ QList Recipe::getReagents(QList ferms) { format = "%1 %2 "; } reagents.append( - format.arg(Measurement::displayAmount(ferms[ii]->amountWithUnits(), - PersistentSettings::Sections::fermentableTable, - PropertyNames::Fermentable::amountWithUnits)) + format.arg(Measurement::displayAmount(ferms[ii]->amountWithUnits())) .arg(ferms[ii]->name()) ); } @@ -2766,9 +2707,7 @@ QList Recipe::getReagents(QList hops, bool firstWort) { for (int i = 0; i < hops.size(); ++i) { if (firstWort && (hops[i]->use() == Hop::Use::First_Wort)) { tmp = QString("%1 %2,") - .arg(Measurement::displayAmount(Measurement::Amount{hops[i]->amount_kg(), Measurement::Units::kilograms}, - PersistentSettings::Sections::hopTable, - PropertyNames::Hop::amount_kg)) + .arg(Measurement::displayAmount(Measurement::Amount{hops[i]->amount_kg(), Measurement::Units::kilograms})) .arg(hops[i]->name()); reagents.append(tmp); } @@ -2787,20 +2726,12 @@ QList Recipe::getReagents(QList< std::shared_ptr > msteps) { QString tmp; if (i + 1 < msteps.size()) { tmp = tr("%1 water to %2, ") - .arg(Measurement::displayAmount(Measurement::Amount{msteps[i]->infuseAmount_l(), Measurement::Units::liters}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::infuseAmount_l)) - .arg(Measurement::displayAmount(Measurement::Amount{msteps[i]->infuseTemp_c(), Measurement::Units::celsius}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::infuseTemp_c)); + .arg(Measurement::displayAmount(Measurement::Amount{msteps[i]->infuseAmount_l(), Measurement::Units::liters})) + .arg(Measurement::displayAmount(Measurement::Amount{msteps[i]->infuseTemp_c(), Measurement::Units::celsius})); } else { tmp = tr("%1 water to %2 ") - .arg(Measurement::displayAmount(Measurement::Amount{msteps[i]->infuseAmount_l(), Measurement::Units::liters}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::infuseAmount_l)) - .arg(Measurement::displayAmount(Measurement::Amount{msteps[i]->infuseTemp_c(), Measurement::Units::celsius}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::infuseTemp_c)); + .arg(Measurement::displayAmount(Measurement::Amount{msteps[i]->infuseAmount_l(), Measurement::Units::liters})) + .arg(Measurement::displayAmount(Measurement::Amount{msteps[i]->infuseTemp_c(), Measurement::Units::celsius})); } reagents.append(tmp); } @@ -2819,15 +2750,11 @@ QStringList Recipe::getReagents(QList salts, Salt::WhenToAdd wanted) { Measurement::Unit const & rightUnit = salts[i]->amountIsWeight() ? Measurement::Units::kilograms : Measurement::Units::liters; if (what == wanted) { tmp = tr("%1 %2, ") - .arg(Measurement::displayAmount(Measurement::Amount{salts[i]->amount(), rightUnit}, - PersistentSettings::Sections::saltTable, - PropertyNames::Salt::amount)) + .arg(Measurement::displayAmount(Measurement::Amount{salts[i]->amount(), rightUnit})) .arg(salts[i]->name()); } else if (what == Salt::WhenToAdd::EQUAL) { tmp = tr("%1 %2, ") - .arg(Measurement::displayAmount(Measurement::Amount{salts[i]->amount(), rightUnit}, - PersistentSettings::Sections::saltTable, - PropertyNames::Salt::amount)) + .arg(Measurement::displayAmount(Measurement::Amount{salts[i]->amount(), rightUnit})) .arg(salts[i]->name()); } else if (what == Salt::WhenToAdd::RATIO) { double ratio = 1.0; @@ -2836,9 +2763,7 @@ QStringList Recipe::getReagents(QList salts, Salt::WhenToAdd wanted) { } double amt = salts[i]->amount() * ratio; tmp = tr("%1 %2, ") - .arg(Measurement::displayAmount(Measurement::Amount{amt, rightUnit}, - PersistentSettings::Sections::saltTable, - PropertyNames::Salt::amount)) + .arg(Measurement::displayAmount(Measurement::Amount{amt, rightUnit})) .arg(salts[i]->name()); } else { continue; diff --git a/src/model/Recipe.h b/src/model/Recipe.h index 07e1567c..a09b32fd 100644 --- a/src/model/Recipe.h +++ b/src/model/Recipe.h @@ -236,7 +236,7 @@ class Recipe : public NamedEntity { Q_PROPERTY(double ABV_pct READ ABV_pct /*WRITE*/ /*NOTIFY changed*/ /*changedABV*/ STORED false) //! \brief The calculated color in SRM. Q_PROPERTY(double color_srm READ color_srm /*WRITE*/ /*NOTIFY changed*/ /*changedColor_srm*/ STORED false) - //! \brief The calculated boil gravity. + //! \brief The calculated boil gravity. .:TBD:. This should perhaps be renamed boilSg for consistency Q_PROPERTY(double boilGrav READ boilGrav /*WRITE*/ /*NOTIFY changed*/ /*changedBoilGrav*/ STORED false) //! \brief The calculated IBUs. Q_PROPERTY(double IBU READ IBU /*WRITE*/ /*NOTIFY changed*/ /*changedIBU*/) diff --git a/src/model/Water.cpp b/src/model/Water.cpp index 330b26fd..44fa6bb8 100644 --- a/src/model/Water.cpp +++ b/src/model/Water.cpp @@ -44,20 +44,20 @@ ObjectStore & Water::getObjectStoreTypedInstance() const { TypeLookup const Water::typeLookup { "Water", { - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::alkalinity , Water::m_alkalinity ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::alkalinityAsHCO3, Water::m_alkalinity_as_hco3), //<< - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::amount , Water::m_amount ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::bicarbonate_ppm , Water::m_bicarbonate_ppm ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::calcium_ppm , Water::m_calcium_ppm ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::chloride_ppm , Water::m_chloride_ppm ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::magnesium_ppm , Water::m_magnesium_ppm ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::mashRO , Water::m_mash_ro ), //<< - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::notes , Water::m_notes ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::ph , Water::m_ph ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::sodium_ppm , Water::m_sodium_ppm ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::spargeRO , Water::m_sparge_ro ), //<< - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::sulfate_ppm , Water::m_sulfate_ppm ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::type , Water::m_type ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::alkalinity , Water::m_alkalinity , Measurement::PhysicalQuantity::VolumeConcentration), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::alkalinityAsHCO3, Water::m_alkalinity_as_hco3, NonPhysicalQuantity::Bool ), //<< + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::amount , Water::m_amount , Measurement::PhysicalQuantity::Volume ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::bicarbonate_ppm , Water::m_bicarbonate_ppm , Measurement::PhysicalQuantity::VolumeConcentration), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::calcium_ppm , Water::m_calcium_ppm , Measurement::PhysicalQuantity::VolumeConcentration), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::chloride_ppm , Water::m_chloride_ppm , Measurement::PhysicalQuantity::VolumeConcentration), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::magnesium_ppm , Water::m_magnesium_ppm , Measurement::PhysicalQuantity::VolumeConcentration), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::mashRO , Water::m_mash_ro , NonPhysicalQuantity::Percentage ), //<< + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::notes , Water::m_notes , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::ph , Water::m_ph , Measurement::PhysicalQuantity::Acidity ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::sodium_ppm , Water::m_sodium_ppm , Measurement::PhysicalQuantity::VolumeConcentration), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::spargeRO , Water::m_sparge_ro , NonPhysicalQuantity::Percentage ), //<< + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::sulfate_ppm , Water::m_sulfate_ppm , Measurement::PhysicalQuantity::VolumeConcentration), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Water::type , Water::m_type ), }, // Parent class lookup &NamedEntity::typeLookup diff --git a/src/model/Water.h b/src/model/Water.h index 27ea1641..13f1bc86 100644 --- a/src/model/Water.h +++ b/src/model/Water.h @@ -122,15 +122,15 @@ class Water : public NamedEntity { Q_PROPERTY(double magnesium_ppm READ magnesium_ppm WRITE setMagnesium_ppm) //! \brief The pH. Q_PROPERTY(double ph READ ph WRITE setPh) - //! \brief The residual alkalinity .:TBD:. Units! + //! \brief The residual alkalinity. Units are ppm .:TBD:. Probably should change name to reflect units! Q_PROPERTY(double alkalinity READ alkalinity WRITE setAlkalinity) //! \brief The notes. Q_PROPERTY(QString notes READ notes WRITE setNotes) //! \brief What kind of water is this Q_PROPERTY(Water::Types type READ type WRITE setType) - //! \brief percent of the mash water that is RO (reverse osmosis) + //! \brief percent of the mash water that is RO (reverse osmosis) .:TBD:. Probably should add _pct suffix Q_PROPERTY(double mashRO READ mashRO WRITE setMashRO) - //! \brief percent of the sparge water that is RO (reverse osmosis) + //! \brief percent of the sparge water that is RO (reverse osmosis) .:TBD:. Probably should add _pct suffix Q_PROPERTY(double spargeRO READ spargeRO WRITE setSpargeRO) //! \brief is the alkalinity measured as HCO3 (bicarbonate) or CO3 (carbonate)? Q_PROPERTY(bool alkalinityAsHCO3 READ alkalinityAsHCO3 WRITE setAlkalinityAsHCO3) diff --git a/src/model/Yeast.cpp b/src/model/Yeast.cpp index 95cc0e3e..f5453f93 100644 --- a/src/model/Yeast.cpp +++ b/src/model/Yeast.cpp @@ -67,22 +67,22 @@ ObjectStore & Yeast::getObjectStoreTypedInstance() const { TypeLookup const Yeast::typeLookup { "Yeast", { - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::addToSecondary , Yeast::m_addToSecondary ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::amount , Yeast::m_amount ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::amountIsWeight , Yeast::m_amountIsWeight ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::attenuation_pct , Yeast::m_attenuation_pct ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::bestFor , Yeast::m_bestFor ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::addToSecondary , Yeast::m_addToSecondary , NonPhysicalQuantity::Bool ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::amount , Yeast::m_amount , NonPhysicalQuantity::Dimensionless), // Not really Dimensionless + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::amountIsWeight , Yeast::m_amountIsWeight , NonPhysicalQuantity::Bool ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::attenuation_pct , Yeast::m_attenuation_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::bestFor , Yeast::m_bestFor , NonPhysicalQuantity::String ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::flocculation , Yeast::m_flocculation ), // PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::flocculationString, Yeast::m_flocculationString), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::form , Yeast::m_form ), // PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::formString , Yeast::m_formString ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::laboratory , Yeast::m_laboratory ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::maxReuse , Yeast::m_maxReuse ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::maxTemperature_c , Yeast::m_maxTemperature_c ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::minTemperature_c , Yeast::m_minTemperature_c ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::notes , Yeast::m_notes ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::productID , Yeast::m_productID ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::timesCultured , Yeast::m_timesCultured ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::laboratory , Yeast::m_laboratory , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::maxReuse , Yeast::m_maxReuse , NonPhysicalQuantity::Count ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::maxTemperature_c , Yeast::m_maxTemperature_c , Measurement::PhysicalQuantity::Temperature ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::minTemperature_c , Yeast::m_minTemperature_c , Measurement::PhysicalQuantity::Temperature ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::notes , Yeast::m_notes , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::productID , Yeast::m_productID , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::timesCultured , Yeast::m_timesCultured , NonPhysicalQuantity::Count ), // PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::typeString , Yeast::m_typeString ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Yeast::type , Yeast::m_type ), }, diff --git a/src/tableModels/BtTableModel.cpp b/src/tableModels/BtTableModel.cpp index a5df5aee..b0db2bb3 100644 --- a/src/tableModels/BtTableModel.cpp +++ b/src/tableModels/BtTableModel.cpp @@ -25,12 +25,13 @@ #include "measurement/Unit.h" #include "measurement/UnitSystem.h" #include "utils/OptionalHelpers.h" +#include "SmartAmounts.h" #include "widgets/UnitAndScalePopUpMenu.h" BtTableModelRecipeObserver::BtTableModelRecipeObserver(QTableView * parent, bool editable, - std::initializer_list > columnIdToInfo) : - BtTableModel{parent, editable, columnIdToInfo}, + std::initializer_list columnInfos) : + BtTableModel{parent, editable, columnInfos}, recObs{nullptr} { return; } @@ -38,75 +39,57 @@ BtTableModelRecipeObserver::BtTableModelRecipeObserver(QTableView * parent, BtTableModelRecipeObserver::~BtTableModelRecipeObserver() = default; //====================================================================================================================== -BtTableModel::BtTableModel(QTableView * parent, - bool editable, - std::initializer_list > columnIdToInfo) : - QAbstractTableModel{parent}, - parentTableWidget{parent}, - editable{editable}, - columnIdToInfo{columnIdToInfo} { + +void BtTableModel::ColumnInfo::setForcedSystemOfMeasurement(std::optional forcedSystemOfMeasurement) const { + SmartAmounts::setForcedSystemOfMeasurement(this->tableModelName, this->columnName, forcedSystemOfMeasurement); return; } -BtTableModel::~BtTableModel() = default; - -std::optional BtTableModel::getForcedSystemOfMeasurementForColumn(int column) const { - QString attribute = this->columnGetAttribute(column); - return attribute.isEmpty() ? std::nullopt : Measurement::getForcedSystemOfMeasurementForField(attribute, - this->objectName()); +void BtTableModel::ColumnInfo::setForcedRelativeScale(std::optional forcedScale) const { + SmartAmounts::setForcedRelativeScale(this->tableModelName, this->columnName, forcedScale); + return; } -std::optional BtTableModel::getForcedRelativeScaleForColumn(int column) const { - QString attribute = this->columnGetAttribute(column); - return attribute.isEmpty() ? std::nullopt : Measurement::getForcedRelativeScaleForField(attribute, - this->objectName()); +std::optional BtTableModel::ColumnInfo::getForcedSystemOfMeasurement() const { + return SmartAmounts::getForcedSystemOfMeasurement(this->tableModelName, this->columnName); } -void BtTableModel::setForcedSystemOfMeasurementForColumn(int column, - std::optional systemOfMeasurement) { - QString attribute = this->columnGetAttribute(column); - if (!attribute.isEmpty()) { - Measurement::setForcedSystemOfMeasurementForField(attribute, this->objectName(), systemOfMeasurement); - // As we're setting/changing the forced SystemOfMeasurement, we want to clear the forced RelativeScale - Measurement::setForcedRelativeScaleForField(attribute, this->objectName(), std::nullopt); - } - return; +std::optional BtTableModel::ColumnInfo::getForcedRelativeScale() const { + return SmartAmounts::getForcedRelativeScale(this->tableModelName, this->columnName); } -void BtTableModel::setForcedRelativeScaleForColumn(int column, - std::optional relativeScale) { - QString attribute = this->columnGetAttribute(column); - if (!attribute.isEmpty()) { - Measurement::setForcedRelativeScaleForField(attribute, this->objectName(), relativeScale); - } +//====================================================================================================================== + +BtTableModel::BtTableModel(QTableView * parent, + bool editable, + std::initializer_list columnInfos) : + QAbstractTableModel{parent}, + parentTableWidget{parent}, + editable{editable}, + m_columnInfos{columnInfos} { return; } -QVariant BtTableModel::getColumName(int column) const { - if (this->columnIdToInfo.contains(column)) { - return QVariant(this->columnIdToInfo.value(column).headerName); - } +BtTableModel::~BtTableModel() = default; - qWarning() << Q_FUNC_INFO << "Bad column:" << column; - return QVariant(); -} +BtTableModel::ColumnInfo const & BtTableModel::getColumnInfo(size_t const columnIndex) const { + // It's a coding error to call this for a non-existent column + Q_ASSERT(columnIndex < this->m_columnInfos.size()); -int BtTableModel::columnCount(QModelIndex const & /*parent*/) const { - return this->columnIdToInfo.size(); + BtTableModel::ColumnInfo const & columnInfo = this->m_columnInfos[columnIndex]; + + // It's a coding error if the info for column N isn't at position N in the vector (in both cases counting from 0) + Q_ASSERT(columnInfo.index == columnIndex); + + return columnInfo; } -QString BtTableModel::columnGetAttribute(int column) const { - if (this->columnIdToInfo.contains(column)) { - return this->columnIdToInfo.value(column).attribute; - } - return ""; +QVariant BtTableModel::getColumnLabel(size_t const columnIndex) const { + return this->getColumnInfo(columnIndex).label; } -BtFieldType BtTableModel::columnGetFieldType(int column) const { - if (this->columnIdToInfo.contains(column)) { - return this->columnIdToInfo.value(column).fieldType; - } - return NonPhysicalQuantity::String; +int BtTableModel::columnCount(QModelIndex const & /*parent*/) const { + return this->m_columnInfos.size(); } void BtTableModel::doContextMenu(QPoint const & point, QHeaderView * hView, QMenu * menu, int selected) { @@ -120,10 +103,10 @@ void BtTableModel::doContextMenu(QPoint const & point, QHeaderView * hView, QMen bool isTopMenu{invoked->parentWidget() == menu}; if (isTopMenu) { // It's the menu, so SystemOfMeasurement - std::optional whatSelected = + std::optional const whatSelected = UnitAndScalePopUpMenu::dataFromQAction(*invoked); qDebug() << Q_FUNC_INFO << "Column" << selected << ", selected SystemOfMeasurement" << whatSelected; - this->setForcedSystemOfMeasurementForColumn(selected, whatSelected); + this->getColumnInfo(selected).setForcedSystemOfMeasurement(whatSelected); // Choosing a forced SystemOfMeasurement resets any selection of forced RelativeScale, but this is handled by // unsetForcedSystemOfMeasurementForColumn() and setForcedSystemOfMeasurementForColumn() } else { @@ -131,7 +114,7 @@ void BtTableModel::doContextMenu(QPoint const & point, QHeaderView * hView, QMen std::optional whatSelected = UnitAndScalePopUpMenu::dataFromQAction(*invoked); qDebug() << Q_FUNC_INFO << "Column" << selected << ", selected RelativeScale" << whatSelected; - this->setForcedRelativeScaleForColumn(selected, whatSelected); + this->getColumnInfo(selected).setForcedRelativeScale(whatSelected); } return; } @@ -142,12 +125,12 @@ void BtTableModel::contextMenu(QPoint const & point) { QHeaderView* hView = qobject_cast(this->sender()); int selected = hView->logicalIndexAt(point); // Only makes sense to offer the pop-up "select scale" menu for physical quantities - BtFieldType fieldType = this->columnGetFieldType(selected); + BtFieldType const fieldType = this->getColumnInfo(selected).fieldType; if (std::holds_alternative(fieldType)) { QMenu * menu = UnitAndScalePopUpMenu::create(parentTableWidget, std::get(fieldType), - this->getForcedSystemOfMeasurementForColumn(selected), - this->getForcedRelativeScaleForColumn(selected)); + this->getColumnInfo(selected).getForcedSystemOfMeasurement(), + this->getColumnInfo(selected).getForcedRelativeScale()); this->doContextMenu(point, hView, menu, selected); } return; diff --git a/src/tableModels/BtTableModel.h b/src/tableModels/BtTableModel.h index faa3f7ee..cf9efd2a 100644 --- a/src/tableModels/BtTableModel.h +++ b/src/tableModels/BtTableModel.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * tableModels/BtTableModel.h is part of Brewken, and is copyright the following authors 2021-2022: + * tableModels/BtTableModel.h is part of Brewken, and is copyright the following authors 2021-2023: * • Matt Young * * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -18,6 +18,7 @@ #pragma once #include +#include #include #include @@ -38,9 +39,9 @@ class Recipe; * * \brief Unfortunately we can't template \c BtTableModel because it inherits from a \c QObject and the Qt meta-object * compiler (moc) can't handle templated classes in QObject-derived classes (though it is fine with templated - * member functions in such classes, as long as the are not signals or slots). We might one day look at + * member functions in such classes, as long as they are not signals or slots). We might one day look at * https://github.com/woboq/verdigris, which overcomes these limitations, but, for now, we live within Qt's - * limitations and try to pull out as much common code as possible using a limited form of multiple inheritance. + * constraints and try to pull out as much common code as possible using a limited form of multiple inheritance. * * QObject * \ @@ -49,7 +50,7 @@ class Recipe; * QAbstractTableModel * \ * \ - * BtTableModel BtTableModelData + * BtTableModel BtTableModelData * / \ / / / * / \ / / / * / MashStepTableModel / / @@ -177,27 +178,77 @@ class BtTableModelData { class BtTableModel : public QAbstractTableModel { Q_OBJECT public: + /** + * \brief This per-column struct / mini-class holds basic info about each column in the table. It also plays a + * slightly similar role as \c SmartLabel. However, there are several important differences, including that + * \c ColumnInfo is \b not a \c QWidget and therefore not a signal emitter. (As mentioned below, it is + * \c QHeaderView that sends us the signal about the user having right-clicked on a column header. We then + * act on the pop-up menu selections directly, rather than \c SmartLabel sending a signal that + * \c SmartLineEdit (and sometimes others) pick up. + * + * NOTE that you usually want to use the SMART_COLUMN_HEADER_INIT macro when constructing + */ struct ColumnInfo { - QString headerName; - BtFieldType fieldType; - QString attribute; + /** + * \brief By analogy with \c editorName in \c SmartLabel and \c SmartLineEdit + */ + char const * const tableModelName; + + /** + * \brief By analogy with \c labelName in \c SmartLabel and \c lineEditName in \c SmartLineEdit + */ + char const * const columnName; + + /** + * \brief By analogy with \c labelFqName in \c SmartLabel and \c lineEditFqName in \c SmartLineEdit + */ + char const * const columnFqName; + + /** + * \brief Each subclass should normally declare its own \c enum \c class \c ColumnIndex to identify its columns. + * We store the column index here as a cross-check that we've got everything in the right order. + */ + size_t const index; + + /** + * \brief The localised text to display in this column header + */ + QString const label; + /** + * \brief What type of data is shown in this column + */ + BtFieldType const fieldType; + + /** + * + */ + std::optional const precision = std::nullopt; + + // Stuff for setting display units and scales -- per column + // I know it looks odd to have const setters, but they are const because they do not change the data in the struct + void setForcedSystemOfMeasurement(std::optional forcedSystemOfMeasurement) const; + void setForcedRelativeScale(std::optional forcedScale) const; + std::optional getForcedSystemOfMeasurement() const; + std::optional getForcedRelativeScale() const; + }; + /** + * \brief + * + * \param parent + * \param editable + * \param columnInfos Needs to be in order + */ BtTableModel(QTableView * parent, bool editable, - std::initializer_list > columnIdToInfo); + std::initializer_list columnInfos); virtual ~BtTableModel(); - // Stuff for setting display units and scales -- per cell column - std::optional getForcedSystemOfMeasurementForColumn(int column) const; - std::optional getForcedRelativeScaleForColumn(int column) const; - void setForcedSystemOfMeasurementForColumn(int column, - std::optional systemOfMeasurement); - void setForcedRelativeScaleForColumn(int column, - std::optional relativeScale); + ColumnInfo const & getColumnInfo(size_t const columnIndex) const; //! \brief Called from \c headerData() - QVariant getColumName(int column) const; + QVariant getColumnLabel(size_t const columnIndex) const; // Per https://doc.qt.io/qt-5/qabstracttablemodel.html, when subclassing QAbstractTableModel, you must implement // rowCount(), columnCount(), and data(). Default implementations of the index() and parent() functions are provided @@ -207,30 +258,39 @@ class BtTableModel : public QAbstractTableModel { virtual int columnCount(QModelIndex const & parent = QModelIndex()) const; private: - QString columnGetAttribute(int column) const; - BtFieldType columnGetFieldType(int column) const; void doContextMenu(QPoint const & point, QHeaderView * hView, QMenu * menu, int selected); public slots: - //! \brief pops the context menu for changing units and scales + //! \brief Receives the \c QWidget::customContextMenuRequested signal from \c QHeaderView to pops the context menu + // for changing units and scales void contextMenu(QPoint const & point); protected: QTableView* parentTableWidget; bool editable; private: - QMap columnIdToInfo; - + /** + * \brief The order of + */ + std::vector const m_columnInfos; }; class BtTableModelRecipeObserver : public BtTableModel { public: BtTableModelRecipeObserver(QTableView * parent, bool editable, - std::initializer_list > columnIdToInfo); + std::initializer_list columnInfos); ~BtTableModelRecipeObserver(); protected: Recipe* recObs; }; + +#define SMART_COLUMN_HEADER_DEFN(tableModelClass, columnName, labelText, btFieldType, ...) \ + BtTableModel::ColumnInfo{#tableModelClass, \ + #columnName, \ + #tableModelClass "::ColumnIndex::" #columnName, \ + static_cast(tableModelClass::ColumnIndex::columnName), \ + labelText, \ + btFieldType} #endif diff --git a/src/tableModels/BtTableModelInventory.cpp b/src/tableModels/BtTableModelInventory.cpp index 54a7fc6e..f763bbd2 100644 --- a/src/tableModels/BtTableModelInventory.cpp +++ b/src/tableModels/BtTableModelInventory.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * tableModels/BtTableModelInventory.cpp is part of Brewken, and is copyright the following authors 2022: + * tableModels/BtTableModelInventory.cpp is part of Brewken, and is copyright the following authors 2022-2023: * • Matt Young * * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -17,8 +17,8 @@ BtTableModelInventory::BtTableModelInventory(QTableView * parent, bool editable, - std::initializer_list > columnIdToInfo) : - BtTableModelRecipeObserver{parent, editable, columnIdToInfo}, + std::initializer_list columnInfos) : + BtTableModelRecipeObserver{parent, editable, columnInfos}, inventoryEditable{false} { return; } diff --git a/src/tableModels/BtTableModelInventory.h b/src/tableModels/BtTableModelInventory.h index 239b8bac..0bf93aa1 100644 --- a/src/tableModels/BtTableModelInventory.h +++ b/src/tableModels/BtTableModelInventory.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * tableModels/BtTableModelInventory.h is part of Brewken, and is copyright the following authors 2022: + * tableModels/BtTableModelInventory.h is part of Brewken, and is copyright the following authors 2022-2023: * • Matt Young * * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -28,7 +28,7 @@ class BtTableModelInventory : public BtTableModelRecipeObserver { public: BtTableModelInventory(QTableView * parent, bool editable, - std::initializer_list > columnIdToInfo); + std::initializer_list columnInfos); ~BtTableModelInventory(); /*! diff --git a/src/tableModels/FermentableTableModel.cpp b/src/tableModels/FermentableTableModel.cpp index 69c2c62b..2fb0a9c2 100644 --- a/src/tableModels/FermentableTableModel.cpp +++ b/src/tableModels/FermentableTableModel.cpp @@ -86,15 +86,17 @@ FermentableTableModel::FermentableTableModel(QTableView* parent, bool editable) BtTableModelInventory{ parent, editable, - {{FERMNAMECOL, {tr("Name"), NonPhysicalQuantity::String, "" }}, - {FERMTYPECOL, {tr("Type"), NonPhysicalQuantity::String, "" }}, - {FERMAMOUNTCOL, {tr("Amount"), Measurement::PhysicalQuantity::Mass, *PropertyNames::Fermentable::amount }}, - {FERMISWEIGHT, {tr("Amount Type"), NonPhysicalQuantity::Bool, "" }}, - {FERMINVENTORYCOL, {tr("Inventory"), Measurement::PhysicalQuantity::Mass, *PropertyNames::NamedEntityWithInventory::inventory}}, - {FERMISMASHEDCOL, {tr("Method"), NonPhysicalQuantity::String, "" }}, - {FERMAFTERBOIL, {tr("Addition"), NonPhysicalQuantity::String, "" }}, - {FERMYIELDCOL, {tr("Yield %"), NonPhysicalQuantity::Percentage, "" }}, - {FERMCOLORCOL, {tr("Color"), Measurement::PhysicalQuantity::Color, *PropertyNames::Fermentable::color_srm }}} + { + SMART_COLUMN_HEADER_DEFN(FermentableTableModel, Name , tr("Name" ), NonPhysicalQuantity::String ), + SMART_COLUMN_HEADER_DEFN(FermentableTableModel, Type , tr("Type" ), NonPhysicalQuantity::String ), + SMART_COLUMN_HEADER_DEFN(FermentableTableModel, Amount , tr("Amount" ), Measurement::PhysicalQuantity::Mass ), + SMART_COLUMN_HEADER_DEFN(FermentableTableModel, Inventory, tr("Amount Type"), NonPhysicalQuantity::Bool ), + SMART_COLUMN_HEADER_DEFN(FermentableTableModel, IsWeight , tr("Inventory" ), Measurement::PhysicalQuantity::Mass ), + SMART_COLUMN_HEADER_DEFN(FermentableTableModel, IsMashed , tr("Method" ), NonPhysicalQuantity::String ), + SMART_COLUMN_HEADER_DEFN(FermentableTableModel, AfterBoil, tr("Addition" ), NonPhysicalQuantity::String ), + SMART_COLUMN_HEADER_DEFN(FermentableTableModel, Yield , tr("Yield %" ), NonPhysicalQuantity::Percentage), + SMART_COLUMN_HEADER_DEFN(FermentableTableModel, Color , tr("Color" ), Measurement::PhysicalQuantity::Color ), + } }, BtTableModelData{}, displayPercentages(false), @@ -116,6 +118,10 @@ FermentableTableModel::FermentableTableModel(QTableView* parent, bool editable) FermentableTableModel::~FermentableTableModel() = default; +BtTableModel::ColumnInfo const & FermentableTableModel::getColumnInfo(FermentableTableModel::ColumnIndex const columnIndex) const { + return this->BtTableModel::getColumnInfo(static_cast(columnIndex)); +} + void FermentableTableModel::observeRecipe(Recipe* rec) { if (this->recObs) { qDebug() << Q_FUNC_INFO << "Unobserve Recipe #" << this->recObs->key() << "(" << this->recObs->name() << ")"; @@ -268,8 +274,8 @@ void FermentableTableModel::changedInventory(int invKey, BtStringConst const & p if (propertyName == PropertyNames::Inventory::amount) { for (int ii = 0; ii < this->rows.size(); ++ii) { if (invKey == this->rows.at(ii)->inventoryId()) { - emit dataChanged(QAbstractItemModel::createIndex(ii, FERMINVENTORYCOL), - QAbstractItemModel::createIndex(ii, FERMINVENTORYCOL)); + emit dataChanged(QAbstractItemModel::createIndex(ii, static_cast(FermentableTableModel::ColumnIndex::Inventory)), + QAbstractItemModel::createIndex(ii, static_cast(FermentableTableModel::ColumnIndex::Inventory))); } } } @@ -288,7 +294,8 @@ void FermentableTableModel::changed(QMetaProperty prop, [[maybe_unused]] QVarian } this->updateTotalGrains(); - emit dataChanged(QAbstractItemModel::createIndex(ii, 0), QAbstractItemModel::createIndex(ii, FERMNUMCOLS - 1)); + emit dataChanged(QAbstractItemModel::createIndex(ii, 0), + QAbstractItemModel::createIndex(ii, this->columnCount() - 1)); if (displayPercentages && rowCount() > 0) { emit headerDataChanged(Qt::Vertical, 0, rowCount() - 1); } @@ -323,14 +330,14 @@ QVariant FermentableTableModel::data(QModelIndex const & index, int role) const return QVariant(); } - int const column = index.column(); - switch (column) { - case FERMNAMECOL: + auto const columnIndex = static_cast(index.column()); + switch (columnIndex) { + case FermentableTableModel::ColumnIndex::Name: if (role == Qt::DisplayRole) { return QVariant(row->name()); } break; - case FERMTYPECOL: + case FermentableTableModel::ColumnIndex::Type: if (role == Qt::DisplayRole) { return QVariant(Fermentable::typeDisplayNames[row->type()]); } @@ -338,7 +345,7 @@ QVariant FermentableTableModel::data(QModelIndex const & index, int role) const return QVariant(static_cast(row->type())); } break; - case FERMINVENTORYCOL: + case FermentableTableModel::ColumnIndex::Inventory: if (role == Qt::DisplayRole) { return QVariant( Measurement::displayAmount(Measurement::Amount{ @@ -347,12 +354,12 @@ QVariant FermentableTableModel::data(QModelIndex const & index, int role) const Measurement::Units::liters }, 3, - this->getForcedSystemOfMeasurementForColumn(column), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), std::nullopt) ); } break; - case FERMAMOUNTCOL: + case FermentableTableModel::ColumnIndex::Amount: if (role == Qt::DisplayRole) { return QVariant( Measurement::displayAmount(Measurement::Amount{ @@ -361,12 +368,12 @@ QVariant FermentableTableModel::data(QModelIndex const & index, int role) const Measurement::Units::liters }, 3, - this->getForcedSystemOfMeasurementForColumn(column), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), std::nullopt) ); } break; - case FERMISWEIGHT: + case FermentableTableModel::ColumnIndex::IsWeight: if (role == Qt::DisplayRole) { return QVariant(descAmountIsWeight[static_cast(row->amountIsWeight())]); } @@ -374,7 +381,7 @@ QVariant FermentableTableModel::data(QModelIndex const & index, int role) const return QVariant(row->amountIsWeight()); } break; - case FERMISMASHEDCOL: + case FermentableTableModel::ColumnIndex::IsMashed: if (role == Qt::DisplayRole) { return QVariant(descIsMashed[static_cast(row->isMashed())]); } @@ -382,7 +389,7 @@ QVariant FermentableTableModel::data(QModelIndex const & index, int role) const return QVariant(row->isMashed()); } break; - case FERMAFTERBOIL: + case FermentableTableModel::ColumnIndex::AfterBoil: if (role == Qt::DisplayRole) { return QVariant(descAddAfterBoil[static_cast(row->addAfterBoil())]); } @@ -390,23 +397,23 @@ QVariant FermentableTableModel::data(QModelIndex const & index, int role) const return QVariant(row->addAfterBoil()); } break; - case FERMYIELDCOL: + case FermentableTableModel::ColumnIndex::Yield: if (role == Qt::DisplayRole) { return QVariant(Measurement::displayQuantity(row->yield_pct(), 3)); } break; - case FERMCOLORCOL: + case FermentableTableModel::ColumnIndex::Color: if (role == Qt::DisplayRole) { return QVariant( Measurement::displayAmount(Measurement::Amount{row->color_srm(), Measurement::Units::srm}, 0, - this->getForcedSystemOfMeasurementForColumn(column), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), std::nullopt) ); } break; default : - qCritical() << Q_FUNC_INFO << "Bad column: " << column; + qCritical() << Q_FUNC_INFO << "Bad column: " << index.column(); break; } return QVariant(); @@ -414,7 +421,7 @@ QVariant FermentableTableModel::data(QModelIndex const & index, int role) const QVariant FermentableTableModel::headerData( int section, Qt::Orientation orientation, int role ) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { - return this->getColumName(section); + return this->getColumnLabel(section); } if (displayPercentages && orientation == Qt::Vertical && role == Qt::DisplayRole) { @@ -437,23 +444,23 @@ Qt::ItemFlags FermentableTableModel::flags(const QModelIndex& index ) const { Qt::ItemFlags constexpr defaults = Qt::ItemIsEnabled; auto row = this->rows[index.row()]; - int col = index.column(); - switch (col) { - case FERMISMASHEDCOL: + auto const columnIndex = static_cast(index.column()); + switch (columnIndex) { + case FermentableTableModel::ColumnIndex::IsMashed: // Ensure that being mashed and being a late addition are mutually exclusive. if (!row->addAfterBoil()) { return (defaults | Qt::ItemIsSelectable | (editable ? Qt::ItemIsEditable : Qt::NoItemFlags) | Qt::ItemIsDragEnabled); } return Qt::ItemIsSelectable | (editable ? Qt::ItemIsEditable : Qt::NoItemFlags) | Qt::ItemIsDragEnabled; - case FERMAFTERBOIL: + case FermentableTableModel::ColumnIndex::AfterBoil: // Ensure that being mashed and being a late addition are mutually exclusive. if (!row->isMashed()) { return (defaults | Qt::ItemIsSelectable | (editable ? Qt::ItemIsEditable : Qt::NoItemFlags) | Qt::ItemIsDragEnabled); } return Qt::ItemIsSelectable | (editable ? Qt::ItemIsEditable : Qt::NoItemFlags) | Qt::ItemIsDragEnabled; - case FERMNAMECOL: + case FermentableTableModel::ColumnIndex::Name: return (defaults | Qt::ItemIsSelectable); - case FERMINVENTORYCOL: + case FermentableTableModel::ColumnIndex::Inventory: return (defaults | (this->isInventoryEditable() ? Qt::ItemIsEditable : Qt::NoItemFlags)); default: return (defaults | Qt::ItemIsSelectable | (editable ? Qt::ItemIsEditable : Qt::NoItemFlags) ); @@ -474,9 +481,9 @@ bool FermentableTableModel::setData(QModelIndex const & index, Measurement::PhysicalQuantity physicalQuantity = row->amountIsWeight() ? Measurement::PhysicalQuantity::Mass: Measurement::PhysicalQuantity::Volume; - int const column = index.column(); - switch (column) { - case FERMNAMECOL: + auto const columnIndex = static_cast(index.column()); + switch (columnIndex) { + case FermentableTableModel::ColumnIndex::Name: retVal = value.canConvert(QVariant::String); if (retVal) { MainWindow::instance().doOrRedoUpdate(*row, @@ -485,7 +492,7 @@ bool FermentableTableModel::setData(QModelIndex const & index, tr("Change Fermentable Name")); } break; - case FERMTYPECOL: + case FermentableTableModel::ColumnIndex::Type: retVal = value.canConvert(QVariant::Int); if (retVal) { // Doing the set via doOrRedoUpdate() saves us from doing a static_cast() here (as the @@ -496,7 +503,7 @@ bool FermentableTableModel::setData(QModelIndex const & index, tr("Change Fermentable Type")); } break; - case FERMINVENTORYCOL: + case FermentableTableModel::ColumnIndex::Inventory: retVal = value.canConvert(QVariant::String); if (retVal) { MainWindow::instance().doOrRedoUpdate( @@ -504,13 +511,13 @@ bool FermentableTableModel::setData(QModelIndex const & index, PropertyNames::NamedEntityWithInventory::inventory, Measurement::qStringToSI(value.toString(), physicalQuantity, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity(), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale()).quantity(), tr("Change Fermentable Inventory Amount") ); } break; - case FERMAMOUNTCOL: + case FermentableTableModel::ColumnIndex::Amount: retVal = value.canConvert(QVariant::String); if (retVal) { // This is where the amount of a fermentable in a recipe gets updated @@ -520,8 +527,8 @@ bool FermentableTableModel::setData(QModelIndex const & index, PropertyNames::Fermentable::amount, Measurement::qStringToSI(value.toString(), physicalQuantity, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity(), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale()).quantity(), tr("Change Fermentable Amount") ); if (rowCount() > 0) { @@ -529,7 +536,7 @@ bool FermentableTableModel::setData(QModelIndex const & index, } } break; - case FERMISWEIGHT: + case FermentableTableModel::ColumnIndex::IsWeight: if (!value.canConvert(QVariant::Bool)) { return false; } @@ -538,7 +545,7 @@ bool FermentableTableModel::setData(QModelIndex const & index, value.toBool(), tr("Change Fermentable Amount Type")); break; - case FERMISMASHEDCOL: + case FermentableTableModel::ColumnIndex::IsMashed: retVal = value.canConvert(QVariant::Bool); if (retVal) { // Doing the set via doOrRedoUpdate() saves us from doing a static_cast() here @@ -549,7 +556,7 @@ bool FermentableTableModel::setData(QModelIndex const & index, tr("Change Fermentable Is Mashed")); } break; - case FERMAFTERBOIL: + case FermentableTableModel::ColumnIndex::AfterBoil: retVal = value.canConvert(QVariant::Bool); if (retVal) { // Doing the set via doOrRedoUpdate() saves us from doing a static_cast() here @@ -560,7 +567,7 @@ bool FermentableTableModel::setData(QModelIndex const & index, tr("Change Add After Boil")); } break; - case FERMYIELDCOL: + case FermentableTableModel::ColumnIndex::Yield: retVal = value.canConvert(QVariant::Double); if (retVal) { MainWindow::instance().doOrRedoUpdate(*row, @@ -569,7 +576,7 @@ bool FermentableTableModel::setData(QModelIndex const & index, tr("Change Yield")); } break; - case FERMCOLORCOL: + case FermentableTableModel::ColumnIndex::Color: retVal = value.canConvert(QVariant::Double); if (retVal) { MainWindow::instance().doOrRedoUpdate( @@ -577,8 +584,8 @@ bool FermentableTableModel::setData(QModelIndex const & index, PropertyNames::Fermentable::color_srm, Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Color, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity(), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale()).quantity(), tr("Change Color") ); } @@ -599,7 +606,8 @@ FermentableItemDelegate::FermentableItemDelegate(QObject* parent) : QItemDelegat QWidget* FermentableItemDelegate::createEditor(QWidget *parent, [[maybe_unused]] QStyleOptionViewItem const & option, QModelIndex const & index) const { - if (index.column() == FERMTYPECOL) { + auto const columnIndex = static_cast(index.column()); + if (columnIndex == FermentableTableModel::ColumnIndex::Type) { QComboBox *box = new QComboBox(parent); for (auto ii : Fermentable::allTypes) { box->addItem(Fermentable::typeDisplayNames[ii]); @@ -610,9 +618,7 @@ QWidget* FermentableItemDelegate::createEditor(QWidget *parent, box->setFocusPolicy(Qt::StrongFocus); return box; - } - - if (index.column() == FERMISMASHEDCOL) { + } else if (columnIndex == FermentableTableModel::ColumnIndex::IsMashed) { QComboBox* box = new QComboBox(parent); QListWidget* list = new QListWidget(parent); list->setResizeMode(QListWidget::Adjust); @@ -627,9 +633,7 @@ QWidget* FermentableItemDelegate::createEditor(QWidget *parent, box->setFocusPolicy(Qt::StrongFocus); return box; - } - - if (index.column() == FERMAFTERBOIL) { + } else if (columnIndex == FermentableTableModel::ColumnIndex::AfterBoil) { QComboBox* box = new QComboBox(parent); box->addItem(descAddAfterBoil[0]); @@ -645,31 +649,26 @@ QWidget* FermentableItemDelegate::createEditor(QWidget *parent, return new QLineEdit(parent); } -void FermentableItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const -{ - int col = index.column(); - - if (col == FERMTYPECOL) { +void FermentableItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { + auto const columnIndex = static_cast(index.column()); + if (columnIndex == FermentableTableModel::ColumnIndex::Type) { QComboBox* box = static_cast(editor); - int ndx = index.model()->data(index, Qt::UserRole).toInt(); - - box->setCurrentIndex(ndx); - } else if (col == FERMISMASHEDCOL || col == FERMAFTERBOIL) { + box->setCurrentIndex(index.model()->data(index, Qt::UserRole).toInt()); + } else if (columnIndex == FermentableTableModel::ColumnIndex::IsMashed || + columnIndex == FermentableTableModel::ColumnIndex::AfterBoil) { QComboBox* box = static_cast(editor); - int ndx = static_cast(index.model()->data(index, Qt::UserRole).toBool()); - box->setCurrentIndex(ndx); + box->setCurrentIndex(static_cast(index.model()->data(index, Qt::UserRole).toBool())); } else { - QLineEdit* line = static_cast(editor); - + QLineEdit* line = qobject_cast(editor); line->setText(index.model()->data(index, Qt::DisplayRole).toString()); } + + return; } void FermentableItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { - int col = index.column(); - - - if (col == FERMTYPECOL) { + auto const columnIndex = static_cast(index.column()); + if (columnIndex == FermentableTableModel::ColumnIndex::Type) { QComboBox* box = qobject_cast(editor); int value = box->currentIndex(); int ndx = model->data(index, Qt::UserRole).toInt(); @@ -678,7 +677,8 @@ void FermentableItemDelegate::setModelData(QWidget *editor, QAbstractItemModel * if (value != ndx) { model->setData(index, value, Qt::EditRole); } - } else if (col == FERMISMASHEDCOL || col == FERMAFTERBOIL) { + } else if (columnIndex == FermentableTableModel::ColumnIndex::IsMashed || + columnIndex == FermentableTableModel::ColumnIndex::AfterBoil) { QComboBox* box = qobject_cast(editor); bool value = box->currentIndex() > 0; bool ndx = model->data(index, Qt::UserRole).toBool(); @@ -691,9 +691,10 @@ void FermentableItemDelegate::setModelData(QWidget *editor, QAbstractItemModel * QLineEdit* line = qobject_cast(editor); if (line->isModified()) { - model->setData(index, line->text(), Qt::EditRole); + model->setData(index, line->text(), Qt::EditRole); } } + return; } @@ -701,4 +702,5 @@ void FermentableItemDelegate::updateEditorGeometry(QWidget * editor, QStyleOptionViewItem const & option, [[maybe_unused]] QModelIndex const & index) const { editor->setGeometry(option.rect); + return; } diff --git a/src/tableModels/FermentableTableModel.h b/src/tableModels/FermentableTableModel.h index 234097c2..d162b979 100644 --- a/src/tableModels/FermentableTableModel.h +++ b/src/tableModels/FermentableTableModel.h @@ -41,8 +41,6 @@ class Fermentable; class Recipe; class FermentableItemDelegate; -enum{FERMNAMECOL, FERMTYPECOL, FERMAMOUNTCOL, FERMINVENTORYCOL, FERMISWEIGHT, FERMISMASHEDCOL, FERMAFTERBOIL, FERMYIELDCOL, FERMCOLORCOL, FERMNUMCOLS /*This one MUST be last*/}; - /*! * \class FermentableTableModel * @@ -52,9 +50,24 @@ class FermentableTableModel : public BtTableModelInventory, public BtTableModelD Q_OBJECT public: + enum class ColumnIndex { + Name , + Type , + Amount , + Inventory, + IsWeight , + IsMashed , + AfterBoil, + Yield , + Color , + }; + FermentableTableModel(QTableView* parent=nullptr, bool editable=true); virtual ~FermentableTableModel(); + //! \brief Casting wrapper for \c BtTableModel::getColumnInfo + ColumnInfo const & getColumnInfo(ColumnIndex const columnIndex) const; + //! \brief Observe a recipe's list of fermentables. void observeRecipe(Recipe* rec); //! \brief If true, we model the database's list of fermentables. diff --git a/src/tableModels/HopTableModel.cpp b/src/tableModels/HopTableModel.cpp index 24f70c75..215acc1c 100644 --- a/src/tableModels/HopTableModel.cpp +++ b/src/tableModels/HopTableModel.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * tableModels/HopTableModel.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * tableModels/HopTableModel.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Daniel Pettersson * • Luke Vincent @@ -52,13 +52,15 @@ HopTableModel::HopTableModel(QTableView * parent, bool editable) : BtTableModelInventory{ parent, editable, - {{HOPNAMECOL, {tr("Name"), NonPhysicalQuantity::String, "" }}, - {HOPALPHACOL, {tr("Alpha %"), NonPhysicalQuantity::Percentage, "" }}, - {HOPAMOUNTCOL, {tr("Amount"), Measurement::PhysicalQuantity::Mass, *PropertyNames::Hop::amount_kg }}, - {HOPINVENTORYCOL, {tr("Inventory"), Measurement::PhysicalQuantity::Mass, *PropertyNames::NamedEntityWithInventory::inventory}}, - {HOPFORMCOL, {tr("Form"), NonPhysicalQuantity::String, "" }}, - {HOPUSECOL, {tr("Use"), NonPhysicalQuantity::String, "" }}, - {HOPTIMECOL, {tr("Time"), Measurement::PhysicalQuantity::Time, *PropertyNames::Hop::time_min }}} + { + SMART_COLUMN_HEADER_DEFN(HopTableModel, Name , tr("Name" ), NonPhysicalQuantity::String ), + SMART_COLUMN_HEADER_DEFN(HopTableModel, Alpha , tr("Alpha %" ), NonPhysicalQuantity::Percentage), + SMART_COLUMN_HEADER_DEFN(HopTableModel, Amount , tr("Amount" ), Measurement::PhysicalQuantity::Mass ), + SMART_COLUMN_HEADER_DEFN(HopTableModel, Inventory, tr("Inventory"), Measurement::PhysicalQuantity::Mass ), + SMART_COLUMN_HEADER_DEFN(HopTableModel, Form , tr("Form" ), NonPhysicalQuantity::String ), + SMART_COLUMN_HEADER_DEFN(HopTableModel, Use , tr("Use" ), NonPhysicalQuantity::String ), + SMART_COLUMN_HEADER_DEFN(HopTableModel, Time , tr("Time" ), Measurement::PhysicalQuantity::Time ), + } }, showIBUs(false) { this->rows.clear(); @@ -81,6 +83,10 @@ HopTableModel::~HopTableModel() { return; } +BtTableModel::ColumnInfo const & HopTableModel::getColumnInfo(HopTableModel::ColumnIndex const columnIndex) const { + return this->BtTableModel::getColumnInfo(static_cast(columnIndex)); +} + void HopTableModel::observeRecipe(Recipe * rec) { if (this->recObs) { disconnect(this->recObs, nullptr, this, nullptr); @@ -215,8 +221,8 @@ void HopTableModel::changedInventory(int invKey, BtStringConst const & propertyN if (propertyName == PropertyNames::Inventory::amount) { for (int ii = 0; ii < this->rows.size(); ++ii) { if (invKey == this->rows.at(ii)->inventoryId()) { - emit dataChanged(QAbstractItemModel::createIndex(ii, HOPINVENTORYCOL), - QAbstractItemModel::createIndex(ii, HOPINVENTORYCOL)); + emit dataChanged(QAbstractItemModel::createIndex(ii, static_cast(HopTableModel::ColumnIndex::Inventory)), + QAbstractItemModel::createIndex(ii, static_cast(HopTableModel::ColumnIndex::Inventory))); } } } @@ -234,7 +240,7 @@ void HopTableModel::changed(QMetaProperty prop, [[maybe_unused]] QVariant val) { } emit dataChanged(QAbstractItemModel::createIndex(ii, 0), - QAbstractItemModel::createIndex(ii, HOPNUMCOLS - 1)); + QAbstractItemModel::createIndex(ii, this->columnCount() - 1)); emit headerDataChanged(Qt::Vertical, ii, ii); return; } @@ -268,35 +274,35 @@ QVariant HopTableModel::data(const QModelIndex & index, int role) const { auto row = this->rows[index.row()]; - int const column = index.column(); - switch (column) { - case HOPNAMECOL: + auto const columnIndex = static_cast(index.column()); + switch (columnIndex) { + case HopTableModel::ColumnIndex::Name: if (role == Qt::DisplayRole) { return QVariant(row->name()); } break; - case HOPALPHACOL: + case HopTableModel::ColumnIndex::Alpha: if (role == Qt::DisplayRole) { return QVariant(Measurement::displayQuantity(row->alpha_pct(), 3)); } break; - case HOPINVENTORYCOL: + case HopTableModel::ColumnIndex::Inventory: if (role == Qt::DisplayRole) { return QVariant(Measurement::displayAmount(Measurement::Amount{row->inventory(), Measurement::Units::kilograms}, 3, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column))); + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale())); } break; - case HOPAMOUNTCOL: + case HopTableModel::ColumnIndex::Amount: if (role == Qt::DisplayRole) { return QVariant(Measurement::displayAmount(Measurement::Amount{row->amount_kg(), Measurement::Units::kilograms}, 3, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column))); + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale())); } break; - case HOPUSECOL: + case HopTableModel::ColumnIndex::Use: if (role == Qt::DisplayRole) { return QVariant(Hop::useDisplayNames[row->use()]); } @@ -304,15 +310,15 @@ QVariant HopTableModel::data(const QModelIndex & index, int role) const { return QVariant(static_cast(row->use())); } break; - case HOPTIMECOL: + case HopTableModel::ColumnIndex::Time: if (role == Qt::DisplayRole) { return QVariant(Measurement::displayAmount(Measurement::Amount{row->time_min(), Measurement::Units::minutes}, 3, std::nullopt, - this->getForcedRelativeScaleForColumn(column))); + this->getColumnInfo(columnIndex).getForcedRelativeScale())); } break; - case HOPFORMCOL: + case HopTableModel::ColumnIndex::Form: if (role == Qt::DisplayRole) { return QVariant(Hop::formDisplayNames[row->form()]); } else if (role == Qt::UserRole) { @@ -320,7 +326,7 @@ QVariant HopTableModel::data(const QModelIndex & index, int role) const { } break; default : - qWarning() << Q_FUNC_INFO << "Bad column: " << column; + qWarning() << Q_FUNC_INFO << "Bad column: " << index.column(); break; } return QVariant(); @@ -328,7 +334,7 @@ QVariant HopTableModel::data(const QModelIndex & index, int role) const { QVariant HopTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { - return this->getColumName(section); + return this->getColumnLabel(section); } if (showIBUs && recObs && orientation == Qt::Vertical && role == Qt::DisplayRole) { QList ibus = recObs->IBUs(); @@ -341,16 +347,15 @@ QVariant HopTableModel::headerData(int section, Qt::Orientation orientation, int } Qt::ItemFlags HopTableModel::flags(const QModelIndex & index) const { - int col = index.column(); - switch (col) { - case HOPNAMECOL: - return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; - case HOPINVENTORYCOL: - return Qt::ItemIsEnabled | (this->isInventoryEditable() ? Qt::ItemIsEditable : Qt::NoItemFlags); - default: - return Qt::ItemIsSelectable | (this->editable ? Qt::ItemIsEditable : Qt::NoItemFlags) | Qt::ItemIsDragEnabled | - Qt::ItemIsEnabled; + auto const columnIndex = static_cast(index.column()); + if (columnIndex == HopTableModel::ColumnIndex::Name) { + return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; } + if (columnIndex == HopTableModel::ColumnIndex::Inventory) { + return Qt::ItemIsEnabled | (this->isInventoryEditable() ? Qt::ItemIsEditable : Qt::NoItemFlags); + } + return Qt::ItemIsSelectable | + (this->editable ? Qt::ItemIsEditable : Qt::NoItemFlags) | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; } bool HopTableModel::setData(const QModelIndex & index, const QVariant & value, int role) { @@ -363,9 +368,9 @@ bool HopTableModel::setData(const QModelIndex & index, const QVariant & value, i auto row = this->rows[index.row()]; - int const column = index.column(); - switch (column) { - case HOPNAMECOL: + auto const columnIndex = static_cast(index.column()); + switch (columnIndex) { + case HopTableModel::ColumnIndex::Name: retVal = value.canConvert(QVariant::String); if (retVal) { MainWindow::instance().doOrRedoUpdate(*row, @@ -374,7 +379,7 @@ bool HopTableModel::setData(const QModelIndex & index, const QVariant & value, i tr("Change Hop Name")); } break; - case HOPALPHACOL: + case HopTableModel::ColumnIndex::Alpha: retVal = value.canConvert(QVariant::Double); if (retVal) { amt = Localization::toDouble(value.toString(), &retVal); @@ -388,7 +393,7 @@ bool HopTableModel::setData(const QModelIndex & index, const QVariant & value, i } break; - case HOPINVENTORYCOL: + case HopTableModel::ColumnIndex::Inventory: retVal = value.canConvert(QVariant::String); if (retVal) { MainWindow::instance().doOrRedoUpdate( @@ -396,13 +401,13 @@ bool HopTableModel::setData(const QModelIndex & index, const QVariant & value, i PropertyNames::NamedEntityWithInventory::inventory, Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Mass, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity(), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale()).quantity(), tr("Change Hop Inventory Amount") ); } break; - case HOPAMOUNTCOL: + case HopTableModel::ColumnIndex::Amount: retVal = value.canConvert(QVariant::String); if (retVal) { MainWindow::instance().doOrRedoUpdate( @@ -410,13 +415,13 @@ bool HopTableModel::setData(const QModelIndex & index, const QVariant & value, i PropertyNames::Hop::amount_kg, Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Mass, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity(), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale()).quantity(), tr("Change Hop Amount") ); } break; - case HOPUSECOL: + case HopTableModel::ColumnIndex::Use: retVal = value.canConvert(QVariant::Int); if (retVal) { MainWindow::instance().doOrRedoUpdate(*row, @@ -425,7 +430,7 @@ bool HopTableModel::setData(const QModelIndex & index, const QVariant & value, i tr("Change Hop Use")); } break; - case HOPFORMCOL: + case HopTableModel::ColumnIndex::Form: retVal = value.canConvert(QVariant::Int); if (retVal) { MainWindow::instance().doOrRedoUpdate(*row, @@ -434,7 +439,7 @@ bool HopTableModel::setData(const QModelIndex & index, const QVariant & value, i tr("Change Hop Form")); } break; - case HOPTIMECOL: + case HopTableModel::ColumnIndex::Time: retVal = value.canConvert(QVariant::String); if (retVal) { MainWindow::instance().doOrRedoUpdate( @@ -443,13 +448,13 @@ bool HopTableModel::setData(const QModelIndex & index, const QVariant & value, i Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Time, std::nullopt, - this->getForcedRelativeScaleForColumn(column)).quantity(), + this->getColumnInfo(columnIndex).getForcedRelativeScale()).quantity(), tr("Change Hop Time") ); } break; default: - qWarning() << Q_FUNC_INFO << "Bad column: " << column; + qWarning() << Q_FUNC_INFO << "Bad columnIndex: " << index.column(); return false; } @@ -468,7 +473,8 @@ HopItemDelegate::HopItemDelegate(QObject * parent) QWidget * HopItemDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & /*option*/, const QModelIndex & index) const { - if (index.column() == HOPUSECOL) { + auto const columnIndex = static_cast(index.column()); + if (columnIndex == HopTableModel::ColumnIndex::Use) { QComboBox * box = new QComboBox(parent); //¥¥¥¥¥ DO THESE PLUS WHERE USED // NOTE: these need to be in the same order as the Hop::Use enum. @@ -481,7 +487,7 @@ QWidget * HopItemDelegate::createEditor(QWidget * parent, const QStyleOptionView box->setSizeAdjustPolicy(QComboBox::AdjustToContents); return box; - } else if (index.column() == HOPFORMCOL) { + } else if (columnIndex == HopTableModel::ColumnIndex::Form) { QComboBox * box = new QComboBox(parent); box->addItem(tr("Leaf")); @@ -497,12 +503,13 @@ QWidget * HopItemDelegate::createEditor(QWidget * parent, const QStyleOptionView } void HopItemDelegate::setEditorData(QWidget * editor, const QModelIndex & index) const { - if (index.column() == HOPUSECOL) { + auto const columnIndex = static_cast(index.column()); + if (columnIndex == HopTableModel::ColumnIndex::Use) { QComboBox * box = static_cast(editor); int ndx = index.model()->data(index, Qt::UserRole).toInt(); box->setCurrentIndex(ndx); - } else if (index.column() == HOPFORMCOL) { + } else if (columnIndex == HopTableModel::ColumnIndex::Form) { QComboBox * box = static_cast(editor); int ndx = index.model()->data(index, Qt::UserRole).toInt(); @@ -511,10 +518,12 @@ void HopItemDelegate::setEditorData(QWidget * editor, const QModelIndex & index) QLineEdit * line = static_cast(editor); line->setText(index.model()->data(index, Qt::DisplayRole).toString()); } + return; } void HopItemDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const { - if (index.column() == HOPUSECOL) { + auto const columnIndex = static_cast(index.column()); + if (columnIndex == HopTableModel::ColumnIndex::Use) { QComboBox * box = static_cast(editor); int value = box->currentIndex(); int ndx = model->data(index, Qt::UserRole).toInt(); @@ -522,7 +531,7 @@ void HopItemDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, if (value != ndx) { model->setData(index, value, Qt::EditRole); } - } else if (index.column() == HOPFORMCOL) { + } else if (columnIndex == HopTableModel::ColumnIndex::Form) { QComboBox * box = static_cast(editor); int value = box->currentIndex(); int ndx = model->data(index, Qt::UserRole).toInt(); @@ -536,9 +545,11 @@ void HopItemDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, model->setData(index, line->text(), Qt::EditRole); } } + return; } void HopItemDelegate::updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & /*index*/) const { editor->setGeometry(option.rect); + return; } diff --git a/src/tableModels/HopTableModel.h b/src/tableModels/HopTableModel.h index eb1ae48e..a6acba98 100644 --- a/src/tableModels/HopTableModel.h +++ b/src/tableModels/HopTableModel.h @@ -37,8 +37,6 @@ class BtStringConst; class HopTableModel; class HopItemDelegate; -enum{HOPNAMECOL, HOPALPHACOL, HOPAMOUNTCOL, HOPINVENTORYCOL, HOPFORMCOL, HOPUSECOL, HOPTIMECOL, HOPNUMCOLS /*This one MUST be last*/}; - /*! * \class HopTableModel * @@ -48,9 +46,22 @@ class HopTableModel : public BtTableModelInventory, public BtTableModelData Q_OBJECT public: + enum class ColumnIndex { + Name , + Alpha , + Amount , + Inventory, + Form , + Use , + Time , + }; + HopTableModel(QTableView* parent=nullptr, bool editable=true); virtual ~HopTableModel(); + //! \brief Casting wrapper for \c BtTableModel::getColumnInfo + ColumnInfo const & getColumnInfo(ColumnIndex const columnIndex) const; + //! \brief Observe a recipe's list of fermentables. void observeRecipe(Recipe* rec); //! \brief If true, we model the database's list of hops. diff --git a/src/tableModels/MashStepTableModel.cpp b/src/tableModels/MashStepTableModel.cpp index 15a9da58..5df97d29 100644 --- a/src/tableModels/MashStepTableModel.cpp +++ b/src/tableModels/MashStepTableModel.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * tableModels/MashStepTableModel.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * tableModels/MashStepTableModel.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Mattias Måhl * • Matt Young @@ -45,12 +45,14 @@ MashStepTableModel::MashStepTableModel(QTableView* parent) : BtTableModel{ parent, false, - {{MASHSTEPNAMECOL, {tr("Name"), NonPhysicalQuantity::String, "" }}, - {MASHSTEPTYPECOL, {tr("Type"), NonPhysicalQuantity::String, "" }}, - {MASHSTEPAMOUNTCOL, {tr("Amount"), Measurement::PhysicalQuantity::Volume, "amount" }}, // Not a real property name - {MASHSTEPTEMPCOL, {tr("Infusion Temp"), Measurement::PhysicalQuantity::Temperature, *PropertyNames::MashStep::infuseTemp_c}}, - {MASHSTEPTARGETTEMPCOL, {tr("Target Temp"), Measurement::PhysicalQuantity::Temperature, *PropertyNames::MashStep::stepTemp_c }}, - {MASHSTEPTIMECOL, {tr("Time"), Measurement::PhysicalQuantity::Time, *PropertyNames::Misc::time }}} + { + SMART_COLUMN_HEADER_DEFN(MashStepTableModel, Name , tr("Name" ), NonPhysicalQuantity::String ), + SMART_COLUMN_HEADER_DEFN(MashStepTableModel, Type , tr("Type" ), NonPhysicalQuantity::String ), + SMART_COLUMN_HEADER_DEFN(MashStepTableModel, Amount , tr("Amount" ), Measurement::PhysicalQuantity::Volume ), + SMART_COLUMN_HEADER_DEFN(MashStepTableModel, Temp , tr("Infusion Temp"), Measurement::PhysicalQuantity::Temperature), + SMART_COLUMN_HEADER_DEFN(MashStepTableModel, TargetTemp, tr("Target Temp" ), Measurement::PhysicalQuantity::Temperature), + SMART_COLUMN_HEADER_DEFN(MashStepTableModel, Time , tr("Time" ), Measurement::PhysicalQuantity::Time ), + } }, mashObs(nullptr) { setObjectName("mashStepTableModel"); @@ -74,6 +76,10 @@ MashStepTableModel::MashStepTableModel(QTableView* parent) : MashStepTableModel::~MashStepTableModel() = default; +BtTableModel::ColumnInfo const & MashStepTableModel::getColumnInfo(MashStepTableModel::ColumnIndex const columnIndex) const { + return this->BtTableModel::getColumnInfo(static_cast(columnIndex)); +} + bool MashStepTableModel::remove(std::shared_ptr mashStep) { int ii {this->rows.indexOf(mashStep)}; @@ -222,8 +228,8 @@ void MashStepTableModel::mashStepChanged(QMetaProperty prop, this->reorderMashStep(this->rows.at(ii), ii); } - emit dataChanged( QAbstractItemModel::createIndex(ii, 0), - QAbstractItemModel::createIndex(ii, MASHSTEPNUMCOLS-1)); + emit dataChanged(QAbstractItemModel::createIndex(ii, 0), + QAbstractItemModel::createIndex(ii, this->columnCount() - 1)); } } @@ -257,13 +263,13 @@ QVariant MashStepTableModel::data(QModelIndex const & index, int role) const { auto row = this->rows[index.row()]; - int const column = index.column(); - switch (column) { - case MASHSTEPNAMECOL: + auto const columnIndex = static_cast(index.column()); + switch (columnIndex) { + case MashStepTableModel::ColumnIndex::Name: return QVariant(row->name()); - case MASHSTEPTYPECOL: + case MashStepTableModel::ColumnIndex::Type: return QVariant(row->typeStringTr()); - case MASHSTEPAMOUNTCOL: + case MashStepTableModel::ColumnIndex::Amount: return QVariant( Measurement::displayAmount( Measurement::Amount{ @@ -271,58 +277,53 @@ QVariant MashStepTableModel::data(QModelIndex const & index, int role) const { Measurement::Units::liters }, 3, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column) + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale() ) ); - case MASHSTEPTEMPCOL: + case MashStepTableModel::ColumnIndex::Temp: if (row->type() == MashStep::Type::Decoction) { return QVariant("---"); } return QVariant( Measurement::displayAmount(Measurement::Amount{row->infuseTemp_c(), Measurement::Units::celsius}, 3, - this->getForcedSystemOfMeasurementForColumn(column), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), std::nullopt) ); - case MASHSTEPTARGETTEMPCOL: + case MashStepTableModel::ColumnIndex::TargetTemp: return QVariant( Measurement::displayAmount(Measurement::Amount{row->stepTemp_c(), Measurement::Units::celsius}, 3, - this->getForcedSystemOfMeasurementForColumn(column), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), std::nullopt) ); - case MASHSTEPTIMECOL: + case MashStepTableModel::ColumnIndex::Time: return QVariant( Measurement::displayAmount(Measurement::Amount{row->stepTime_min(), Measurement::Units::minutes}, 3, std::nullopt, - this->getForcedRelativeScaleForColumn(column)) + this->getColumnInfo(columnIndex).getForcedRelativeScale()) ); default : - qWarning() << Q_FUNC_INFO << "Bad column: " << column; + qWarning() << Q_FUNC_INFO << "Bad column: " << index.column(); return QVariant(); } } QVariant MashStepTableModel::headerData( int section, Qt::Orientation orientation, int role ) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { - return this->getColumName(section); + return this->getColumnLabel(section); } return QVariant(); } -Qt::ItemFlags MashStepTableModel::flags(const QModelIndex& index ) const -{ - int col = index.column(); - switch(col) - { - case MASHSTEPNAMECOL: - return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; - default: - return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | - Qt::ItemIsEnabled; +Qt::ItemFlags MashStepTableModel::flags(const QModelIndex& index ) const { + auto const columnIndex = static_cast(index.column()); + if (columnIndex == MashStepTableModel::ColumnIndex::Name) { + return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; } + return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; } bool MashStepTableModel::setData(QModelIndex const & index, QVariant const & value, int role) { @@ -337,9 +338,9 @@ bool MashStepTableModel::setData(QModelIndex const & index, QVariant const & val auto row = this->rows[index.row()]; - int const column = index.column(); - switch (column) { - case MASHSTEPNAMECOL: + auto const columnIndex = static_cast(index.column()); + switch (columnIndex) { + case MashStepTableModel::ColumnIndex::Name: if (value.canConvert(QVariant::String)) { MainWindow::instance().doOrRedoUpdate(*row, PropertyNames::NamedEntity::name, @@ -349,7 +350,7 @@ bool MashStepTableModel::setData(QModelIndex const & index, QVariant const & val } return false; - case MASHSTEPTYPECOL: + case MashStepTableModel::ColumnIndex::Type: if (value.canConvert(QVariant::Int)) { MainWindow::instance().doOrRedoUpdate(*row, PropertyNames::MashStep::type, @@ -359,7 +360,7 @@ bool MashStepTableModel::setData(QModelIndex const & index, QVariant const & val } return false; - case MASHSTEPAMOUNTCOL: + case MashStepTableModel::ColumnIndex::Amount: if (value.canConvert(QVariant::String)) { if (row->type() == MashStep::Type::Decoction ) { MainWindow::instance().doOrRedoUpdate( @@ -367,8 +368,8 @@ bool MashStepTableModel::setData(QModelIndex const & index, QVariant const & val PropertyNames::MashStep::decoctionAmount_l, Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Volume, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity(), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale()).quantity(), tr("Change Mash Step Decoction Amount") ); } else { @@ -377,8 +378,8 @@ bool MashStepTableModel::setData(QModelIndex const & index, QVariant const & val PropertyNames::MashStep::infuseAmount_l, Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Volume, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity(), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale()).quantity(), tr("Change Mash Step Infuse Amount") ); } @@ -386,22 +387,22 @@ bool MashStepTableModel::setData(QModelIndex const & index, QVariant const & val } return false; - case MASHSTEPTEMPCOL: + case MashStepTableModel::ColumnIndex::Temp: if (value.canConvert(QVariant::String) && row->type() != MashStep::Type::Decoction) { MainWindow::instance().doOrRedoUpdate( *row, PropertyNames::MashStep::infuseTemp_c, Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Temperature, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity(), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale()).quantity(), tr("Change Mash Step Infuse Temp") ); return true; } return false; - case MASHSTEPTARGETTEMPCOL: + case MashStepTableModel::ColumnIndex::TargetTemp: if (value.canConvert(QVariant::String)) { // Two changes, but we want to group together as one undo/redo step // @@ -412,16 +413,16 @@ bool MashStepTableModel::setData(QModelIndex const & index, QVariant const & val PropertyNames::MashStep::stepTemp_c, Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Temperature, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity(), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale()).quantity(), tr("Change Mash Step Temp") ); new SimpleUndoableUpdate(*row, PropertyNames::MashStep::endTemp_c, Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Temperature, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity(), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale()).quantity(), tr("Change Mash Step End Temp"), targetTempUpdate); MainWindow::instance().doOrRedoUpdate(targetTempUpdate); @@ -429,15 +430,15 @@ bool MashStepTableModel::setData(QModelIndex const & index, QVariant const & val } return false; - case MASHSTEPTIMECOL: + case MashStepTableModel::ColumnIndex::Time: if (value.canConvert(QVariant::String)) { MainWindow::instance().doOrRedoUpdate( *row, PropertyNames::MashStep::stepTime_min, Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Time, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity(), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale()).quantity(), tr("Change Mash Step Time") ); return true; @@ -473,10 +474,11 @@ MashStepItemDelegate::MashStepItemDelegate(QObject* parent) : QItemDelegate(pare return; } -QWidget* MashStepItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/*option*/, const QModelIndex &index) const -{ - if (index.column() == MASHSTEPTYPECOL ) - { +QWidget* MashStepItemDelegate::createEditor(QWidget * parent, + QStyleOptionViewItem const &/*option*/, + QModelIndex const & index) const { + auto const columnIndex = static_cast(index.column()); + if (columnIndex == MashStepTableModel::ColumnIndex::Type) { QComboBox *box = new QComboBox(parent); foreach( QString mtype, MashStep::types ) @@ -486,51 +488,50 @@ QWidget* MashStepItemDelegate::createEditor(QWidget *parent, const QStyleOptionV return box; } - else - return new QLineEdit(parent); + + return new QLineEdit(parent); } -void MashStepItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const -{ - if (index.column() == MASHSTEPTYPECOL ) - { +void MashStepItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { + auto const columnIndex = static_cast(index.column()); + if (columnIndex == MashStepTableModel::ColumnIndex::Type) { QComboBox* box = qobject_cast(editor); QString text = index.model()->data(index, Qt::DisplayRole).toString(); int index = box->findText(text); box->setCurrentIndex(index); - } - else - { + } else { QLineEdit* line = qobject_cast(editor); line->setText(index.model()->data(index, Qt::DisplayRole).toString()); } - + return; } -void MashStepItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const -{ +void MashStepItemDelegate::setModelData(QWidget * editor, + QAbstractItemModel * model, + QModelIndex const & index) const { QStringList typesTr = QStringList() << QObject::tr("Infusion") << QObject::tr("Temperature") << QObject::tr("Decoction"); - if (index.column() == MASHSTEPTYPECOL ) - { + auto const columnIndex = static_cast(index.column()); + if (columnIndex == MashStepTableModel::ColumnIndex::Type) { QComboBox* box = qobject_cast(editor); int ndx = box->currentIndex(); int curr = typesTr.indexOf(model->data(index,Qt::DisplayRole).toString()); - - if ( ndx != curr ) + if ( ndx != curr ) { model->setData(index, ndx, Qt::EditRole); - } - else - { + } + } else { QLineEdit* line = qobject_cast(editor); - - if ( line->isModified() ) + if ( line->isModified() ) { model->setData(index, line->text(), Qt::EditRole); + } } + return; } -void MashStepItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex& /*index*/) const -{ +void MashStepItemDelegate::updateEditorGeometry(QWidget *editor, + QStyleOptionViewItem const & option, + QModelIndex const & /*index*/) const { editor->setGeometry(option.rect); + return; } diff --git a/src/tableModels/MashStepTableModel.h b/src/tableModels/MashStepTableModel.h index 2f05b364..d5d7dd0a 100644 --- a/src/tableModels/MashStepTableModel.h +++ b/src/tableModels/MashStepTableModel.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * tableModels/MashStepTableModel.h is part of Brewken, and is copyright the following authors 2009-2022: + * tableModels/MashStepTableModel.h is part of Brewken, and is copyright the following authors 2009-2023: * • Jeff Bailey * • Matt Young * • Mik Firestone @@ -35,8 +35,6 @@ class MashStepItemDelegate; -enum{ MASHSTEPNAMECOL, MASHSTEPTYPECOL, MASHSTEPAMOUNTCOL, MASHSTEPTEMPCOL, MASHSTEPTARGETTEMPCOL, MASHSTEPTIMECOL, MASHSTEPNUMCOLS /*This one MUST be last*/}; - /*! * \class MashStepTableModel * @@ -46,9 +44,21 @@ class MashStepTableModel : public BtTableModel, public BtTableModelData{} { this->rows.clear(); @@ -69,6 +71,10 @@ MiscTableModel::MiscTableModel(QTableView* parent, bool editable) : MiscTableModel::~MiscTableModel() = default; +BtTableModel::ColumnInfo const & MiscTableModel::getColumnInfo(MiscTableModel::ColumnIndex const columnIndex) const { + return this->BtTableModel::getColumnInfo(static_cast(columnIndex)); +} + void MiscTableModel::observeRecipe(Recipe* rec) { if (this->recObs) { qDebug() << Q_FUNC_INFO << "Unwatching Recipe" << this->recObs; @@ -197,14 +203,14 @@ QVariant MiscTableModel::data(QModelIndex const & index, int role) const { auto row = this->rows[index.row()]; // Deal with the column and return the right data. - int const column = index.column(); - switch (column) { - case MISCNAMECOL: + auto const columnIndex = static_cast(index.column()); + switch (columnIndex) { + case MiscTableModel::ColumnIndex::Name: if (role == Qt::DisplayRole) { return QVariant(row->name()); } break; - case MISCTYPECOL: + case MiscTableModel::ColumnIndex::Type: if (role == Qt::DisplayRole) { return QVariant(row->typeStringTr()); } @@ -212,7 +218,7 @@ QVariant MiscTableModel::data(QModelIndex const & index, int role) const { return QVariant(static_cast(row->type())); } break; - case MISCUSECOL: + case MiscTableModel::ColumnIndex::Use: if (role == Qt::DisplayRole) { return QVariant(row->useStringTr()); } @@ -220,15 +226,15 @@ QVariant MiscTableModel::data(QModelIndex const & index, int role) const { return QVariant(static_cast(row->use())); } return QVariant(); - case MISCTIMECOL: + case MiscTableModel::ColumnIndex::Time: if (role == Qt::DisplayRole) { return QVariant(Measurement::displayAmount(Measurement::Amount{row->time(), Measurement::Units::minutes}, 3, std::nullopt, - this->getForcedRelativeScaleForColumn(column))); + this->getColumnInfo(columnIndex).getForcedRelativeScale())); } break; - case MISCINVENTORYCOL: + case MiscTableModel::ColumnIndex::Inventory: if (role == Qt::DisplayRole) { return QVariant( Measurement::displayAmount(Measurement::Amount{ @@ -237,12 +243,12 @@ QVariant MiscTableModel::data(QModelIndex const & index, int role) const { Measurement::Units::liters }, 3, - this->getForcedSystemOfMeasurementForColumn(column), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), std::nullopt) ); } break; - case MISCAMOUNTCOL: + case MiscTableModel::ColumnIndex::Amount: if (role == Qt::DisplayRole) { return QVariant( Measurement::displayAmount(Measurement::Amount{ @@ -251,12 +257,12 @@ QVariant MiscTableModel::data(QModelIndex const & index, int role) const { Measurement::Units::liters }, 3, - this->getForcedSystemOfMeasurementForColumn(column), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), std::nullopt) ); } break; - case MISCISWEIGHT: + case MiscTableModel::ColumnIndex::IsWeight: if (role == Qt::DisplayRole) { return QVariant(row->amountTypeStringTr()); } @@ -265,7 +271,7 @@ QVariant MiscTableModel::data(QModelIndex const & index, int role) const { } break; default: - qWarning() << Q_FUNC_INFO << "Bad model index. column = " << column; + qWarning() << Q_FUNC_INFO << "Bad model index. column = " << index.column(); break; } return QVariant(); @@ -273,22 +279,21 @@ QVariant MiscTableModel::data(QModelIndex const & index, int role) const { QVariant MiscTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { - return this->getColumName(section); + return this->getColumnLabel(section); } return QVariant(); } Qt::ItemFlags MiscTableModel::flags(QModelIndex const & index) const { - int col = index.column(); - Qt::ItemFlags defaults = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled; - switch (col) { - case MISCNAMECOL: - return defaults; - case MISCINVENTORYCOL: - return (defaults | (this->isInventoryEditable() ? Qt::ItemIsEditable : Qt::NoItemFlags)); - default: - return defaults | (editable ? Qt::ItemIsEditable : Qt::NoItemFlags); + Qt::ItemFlags const defaults = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled; + auto const columnIndex = static_cast(index.column()); + if (columnIndex == MiscTableModel::ColumnIndex::Name) { + return defaults; + } + if (columnIndex == MiscTableModel::ColumnIndex::Inventory) { + return (defaults | (this->isInventoryEditable() ? Qt::ItemIsEditable : Qt::NoItemFlags)); } + return defaults | (this->editable ? Qt::ItemIsEditable : Qt::NoItemFlags); } bool MiscTableModel::setData(QModelIndex const & index, @@ -304,9 +309,9 @@ bool MiscTableModel::setData(QModelIndex const & index, Measurement::PhysicalQuantity physicalQuantity = row->amountIsWeight() ? Measurement::PhysicalQuantity::Mass: Measurement::PhysicalQuantity::Volume; - int column = index.column(); - switch (column) { - case MISCNAMECOL: + auto const columnIndex = static_cast(index.column()); + switch (columnIndex) { + case MiscTableModel::ColumnIndex::Name: if (value.canConvert(QVariant::String)) { MainWindow::instance().doOrRedoUpdate(*row, PropertyNames::NamedEntity::name, @@ -316,7 +321,7 @@ bool MiscTableModel::setData(QModelIndex const & index, return false; } break; - case MISCTYPECOL: + case MiscTableModel::ColumnIndex::Type: if (!value.canConvert(QVariant::Int)) { return false; } @@ -325,7 +330,7 @@ bool MiscTableModel::setData(QModelIndex const & index, value.toInt(), tr("Change Misc Type")); break; - case MISCUSECOL: + case MiscTableModel::ColumnIndex::Use: if (!value.canConvert(QVariant::Int)) { return false; } @@ -334,7 +339,7 @@ bool MiscTableModel::setData(QModelIndex const & index, value.toInt(), tr("Change Misc Use")); break; - case MISCTIMECOL: + case MiscTableModel::ColumnIndex::Time: if (!value.canConvert(QVariant::String)) { return false; } @@ -343,12 +348,12 @@ bool MiscTableModel::setData(QModelIndex const & index, PropertyNames::Misc::time, Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Time, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity(), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale()).quantity(), tr("Change Misc Time") ); break; - case MISCINVENTORYCOL: + case MiscTableModel::ColumnIndex::Inventory: if (!value.canConvert(QVariant::String)) { return false; } @@ -357,12 +362,12 @@ bool MiscTableModel::setData(QModelIndex const & index, PropertyNames::NamedEntityWithInventory::inventory, Measurement::qStringToSI(value.toString(), physicalQuantity, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity(), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale()).quantity(), tr("Change Misc Inventory Amount") ); break; - case MISCAMOUNTCOL: + case MiscTableModel::ColumnIndex::Amount: if (!value.canConvert(QVariant::String)) { return false; } @@ -371,12 +376,12 @@ bool MiscTableModel::setData(QModelIndex const & index, PropertyNames::Misc::amount, Measurement::qStringToSI(value.toString(), physicalQuantity, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity(), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale()).quantity(), tr("Change Misc Amount") ); break; - case MISCISWEIGHT: + case MiscTableModel::ColumnIndex::IsWeight: if (!value.canConvert(QVariant::Int)) { return false; } @@ -397,8 +402,8 @@ void MiscTableModel::changedInventory(int invKey, BtStringConst const & property if (propertyName == PropertyNames::Inventory::amount) { for (int ii = 0; ii < this->rows.size(); ++ii) { if (invKey == this->rows.at(ii)->inventoryId()) { - emit dataChanged(QAbstractItemModel::createIndex(ii, MISCINVENTORYCOL), - QAbstractItemModel::createIndex(ii, MISCINVENTORYCOL)); + emit dataChanged(QAbstractItemModel::createIndex(ii, static_cast(MiscTableModel::ColumnIndex::Inventory)), + QAbstractItemModel::createIndex(ii, static_cast(MiscTableModel::ColumnIndex::Inventory))); } } } @@ -410,8 +415,8 @@ void MiscTableModel::changed(QMetaProperty prop, [[maybe_unused]] QVariant val) if (miscSender) { int ii = this->findIndexOf(miscSender); if (ii >= 0) { - emit dataChanged( QAbstractItemModel::createIndex(ii, 0), - QAbstractItemModel::createIndex(ii, MISCNUMCOLS-1) ); + emit dataChanged(QAbstractItemModel::createIndex(ii, 0), + QAbstractItemModel::createIndex(ii, this->columnCount() - 1) ); } return; } @@ -441,8 +446,8 @@ MiscItemDelegate::MiscItemDelegate(QObject* parent) : QItemDelegate(parent) { QWidget* MiscItemDelegate::createEditor(QWidget *parent, QStyleOptionViewItem const & /*option*/, QModelIndex const & index) const { - int const column = index.column(); - if (column == MISCTYPECOL) { + auto const columnIndex = static_cast(index.column()); + if (columnIndex == MiscTableModel::ColumnIndex::Type) { QComboBox *box = new QComboBox(parent); box->addItem(tr("Spice")); box->addItem(tr("Fining")); @@ -455,7 +460,7 @@ QWidget* MiscItemDelegate::createEditor(QWidget *parent, return box; } - if (column == MISCUSECOL) { + if (columnIndex == MiscTableModel::ColumnIndex::Use) { QComboBox *box = new QComboBox(parent); box->addItem(tr("Boil")); @@ -468,7 +473,7 @@ QWidget* MiscItemDelegate::createEditor(QWidget *parent, return box; } - if (column == MISCISWEIGHT) { + if (columnIndex == MiscTableModel::ColumnIndex::IsWeight) { QComboBox *box = new QComboBox(parent); box->addItem(tr("Weight")); @@ -482,45 +487,51 @@ QWidget* MiscItemDelegate::createEditor(QWidget *parent, } void MiscItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { - int const column = index.column(); + auto const columnIndex = static_cast(index.column()); - if (column == MISCTYPECOL || column == MISCUSECOL || column == MISCISWEIGHT) - { + if (columnIndex == MiscTableModel::ColumnIndex::Type || + columnIndex == MiscTableModel::ColumnIndex::Use || + columnIndex == MiscTableModel::ColumnIndex::IsWeight) { QComboBox* box = qobject_cast(editor); - if (box == nullptr ) + if (box == nullptr) { return; + } box->setCurrentIndex(index.model()->data(index, Qt::UserRole).toInt()); - } - else - { + } else { QLineEdit* line = static_cast(editor); line->setText(index.model()->data(index, Qt::DisplayRole).toString()); } + + return; } void MiscItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { - int column = index.column(); - if (column == MISCTYPECOL || column == MISCUSECOL || column == MISCISWEIGHT) - { + auto const columnIndex = static_cast(index.column()); + if (columnIndex == MiscTableModel::ColumnIndex::Type || + columnIndex == MiscTableModel::ColumnIndex::Use || + columnIndex == MiscTableModel::ColumnIndex::IsWeight) { QComboBox* box = static_cast(editor); int ndx = box->currentIndex(); int curr = model->data(index, Qt::UserRole).toInt(); - if ( curr != ndx ) + if (curr != ndx) { model->setData(index, ndx, Qt::EditRole); - } - else - { + } + } else { QLineEdit* line = static_cast(editor); - if ( line->isModified() ) + if (line->isModified()) { model->setData(index, line->text(), Qt::EditRole); + } } + + return; } void MiscItemDelegate::updateEditorGeometry(QWidget * editor, QStyleOptionViewItem const & option, [[maybe_unused]] QModelIndex const & index) const { editor->setGeometry(option.rect); + return; } diff --git a/src/tableModels/MiscTableModel.h b/src/tableModels/MiscTableModel.h index 4fc29feb..5f3f868b 100644 --- a/src/tableModels/MiscTableModel.h +++ b/src/tableModels/MiscTableModel.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * tableModels/MiscTableModel.h is part of Brewken, and is copyright the following authors 2009-2022: + * tableModels/MiscTableModel.h is part of Brewken, and is copyright the following authors 2009-2023: * • Jeff Bailey * • Matt Young * • Mik Firestone @@ -43,8 +43,6 @@ class Misc; class MiscTableWidget; class Recipe; -enum{MISCNAMECOL, MISCTYPECOL, MISCUSECOL, MISCTIMECOL, MISCAMOUNTCOL, MISCINVENTORYCOL, MISCISWEIGHT, MISCNUMCOLS /*This one MUST be last*/}; - /*! * \class MiscTableModel * @@ -54,9 +52,22 @@ class MiscTableModel : public BtTableModelInventory, public BtTableModelData{} { setObjectName("saltTable"); QHeaderView* headerView = parentTableWidget->horizontalHeader(); headerView->setContextMenuPolicy(Qt::CustomContextMenu); - headerView->setMinimumSectionSize(parent->width()/SALTNUMCOLS); + headerView->setMinimumSectionSize(parent->width()/this->columnCount()); headerView->setSectionResizeMode(QHeaderView::ResizeToContents); parentTableWidget->setWordWrap(false); @@ -87,8 +91,11 @@ SaltTableModel::~SaltTableModel() { return; } -void SaltTableModel::observeRecipe(Recipe* rec) -{ +BtTableModel::ColumnInfo const & SaltTableModel::getColumnInfo(SaltTableModel::ColumnIndex const columnIndex) const { + return this->BtTableModel::getColumnInfo(static_cast(columnIndex)); +} + +void SaltTableModel::observeRecipe(Recipe* rec) { if ( this->recObs ) { QObject::disconnect( this->recObs, nullptr, this, nullptr ); removeAll(); @@ -102,6 +109,7 @@ void SaltTableModel::observeRecipe(Recipe* rec) spargePct = this->recObs->mash()->totalSpargeAmount_l()/this->recObs->mash()->totalInfusionAmount_l(); } } + return; } void SaltTableModel::addSalt(std::shared_ptr salt) { @@ -118,6 +126,7 @@ void SaltTableModel::addSalt(std::shared_ptr salt) { parentTableWidget->resizeColumnsToContents(); parentTableWidget->resizeRowsToContents(); } + return; } void SaltTableModel::addSalts(QList > salts) { @@ -139,6 +148,7 @@ void SaltTableModel::addSalts(QList > salts) { parentTableWidget->resizeColumnsToContents(); parentTableWidget->resizeRowsToContents(); } + return; } void SaltTableModel::catchSalt() { @@ -350,7 +360,7 @@ void SaltTableModel::changed(QMetaProperty prop, [[maybe_unused]] QVariant val) int ii = this->findIndexOf(saltSender); if (ii >= 0) { emit dataChanged(QAbstractItemModel::createIndex(ii, 0), - QAbstractItemModel::createIndex(ii, SALTNUMCOLS-1)); + QAbstractItemModel::createIndex(ii, this->columnCount()-1)); emit headerDataChanged(Qt::Vertical, ii, ii); } return; @@ -384,9 +394,9 @@ QVariant SaltTableModel::data(QModelIndex const & index, int role) const { auto row = this->rows[index.row()]; - int column = index.column(); - switch (column) { - case SALTNAMECOL: + auto const columnIndex = static_cast(index.column()); + switch (columnIndex) { + case SaltTableModel::ColumnIndex::Name: if (role == Qt::DisplayRole) { return QVariant(saltNames.at(static_cast(row->type()))); } @@ -394,7 +404,7 @@ QVariant SaltTableModel::data(QModelIndex const & index, int role) const { return QVariant(static_cast(row->type())); } return QVariant(); - case SALTAMOUNTCOL: + case SaltTableModel::ColumnIndex::Amount: if (role != Qt::DisplayRole) { return QVariant(); } @@ -405,11 +415,11 @@ QVariant SaltTableModel::data(QModelIndex const & index, int role) const { row->amountIsWeight() ? Measurement::Units::kilograms : Measurement::Units::liters }, 3, - this->getForcedSystemOfMeasurementForColumn(column), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), std::nullopt ) ); - case SALTADDTOCOL: + case SaltTableModel::ColumnIndex::AddTo: if (role == Qt::DisplayRole) { return QVariant( addToName.at(static_cast(row->whenToAdd()))); } @@ -417,33 +427,32 @@ QVariant SaltTableModel::data(QModelIndex const & index, int role) const { return QVariant(static_cast(row->whenToAdd())); } return QVariant(); - case SALTPCTACIDCOL: + case SaltTableModel::ColumnIndex::PctAcid: if (role == Qt::DisplayRole && row->isAcid()) { return QVariant( row->percentAcid() ); } return QVariant(); default : - qWarning() << Q_FUNC_INFO << "Bad column: " << column; + qWarning() << Q_FUNC_INFO << "Bad column: " << index.column(); return QVariant(); } } QVariant SaltTableModel::headerData( int section, Qt::Orientation orientation, int role ) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { - return this->getColumName(section); + return this->getColumnLabel(section); } return QVariant(); } -Qt::ItemFlags SaltTableModel::flags(const QModelIndex& index ) const -{ +Qt::ItemFlags SaltTableModel::flags(const QModelIndex& index) const { // Q_UNUSED(index) - if (index.row() >= this->rows.size() ) + if (index.row() >= this->rows.size() ) { return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; + } - auto row = this->rows[index.row()]; - - if ( !row->isAcid() && index.column() == SALTPCTACIDCOL ) { + auto const row = this->rows[index.row()]; + if (!row->isAcid() && index.column() == static_cast(SaltTableModel::ColumnIndex::PctAcid)) { return Qt::NoItemFlags; } return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; @@ -458,9 +467,9 @@ bool SaltTableModel::setData(QModelIndex const & index, QVariant const & value, auto row = this->rows[index.row()]; - int const column = index.column(); - switch (column) { - case SALTNAMECOL: + auto const columnIndex = static_cast(index.column()); + switch (columnIndex) { + case SaltTableModel::ColumnIndex::Name: retval = value.canConvert(QVariant::Int); if (retval) { int newType = value.toInt(); @@ -477,26 +486,26 @@ bool SaltTableModel::setData(QModelIndex const & index, QVariant const & value, } } break; - case SALTAMOUNTCOL: + case SaltTableModel::ColumnIndex::Amount: retval = value.canConvert(QVariant::Double); if (retval) { row->setAmount( Measurement::qStringToSI( value.toString(), row->amountIsWeight() ? Measurement::PhysicalQuantity::Mass : Measurement::PhysicalQuantity::Volume, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column) + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale() ).quantity() ); } break; - case SALTADDTOCOL: + case SaltTableModel::ColumnIndex::AddTo: retval = value.canConvert(QVariant::Int); if (retval) { row->setWhenToAdd( static_cast(value.toInt()) ); } break; - case SALTPCTACIDCOL: + case SaltTableModel::ColumnIndex::PctAcid: retval = row->isAcid() && value.canConvert(QVariant::Double); if (retval) { row->setPercentAcid(value.toDouble()); @@ -504,7 +513,7 @@ bool SaltTableModel::setData(QModelIndex const & index, QVariant const & value, break; default: retval = false; - qWarning() << tr("Bad column: %1").arg(index.column()); + qWarning() << Q_FUNC_INFO << "Bad column:" << index.column(); } if ( retval && row->whenToAdd() != Salt::WhenToAdd::NEVER ) { @@ -532,14 +541,16 @@ void SaltTableModel::saveAndClose() { SaltItemDelegate::SaltItemDelegate(QObject* parent) : QItemDelegate(parent), - m_mash(nullptr) -{ + m_mash(nullptr) { + return; } -QWidget* SaltItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem& option, const QModelIndex& index) const -{ +QWidget* SaltItemDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem& option, + const QModelIndex& index) const { Q_UNUSED(option) - if ( index.column() == SALTNAMECOL ) { + auto const columnIndex = static_cast(index.column()); + if (columnIndex == SaltTableModel::ColumnIndex::Name) { QComboBox *box = new QComboBox(parent); box->addItem(tr("NONE") , static_cast(Salt::Types::NONE )); @@ -555,9 +566,9 @@ QWidget* SaltItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewI box->setMinimumWidth( box->minimumSizeHint().width()); box->setSizeAdjustPolicy(QComboBox::AdjustToContents); return box; - } - else if ( index.column() == SALTADDTOCOL ) { + + if (columnIndex == SaltTableModel::ColumnIndex::AddTo) { QComboBox *box = new QComboBox(parent); box->addItem(tr("Never"), static_cast(Salt::WhenToAdd::NEVER )); @@ -581,30 +592,27 @@ QWidget* SaltItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewI return box; } - else { - return new QLineEdit(parent); - } -} -void SaltItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const -{ - int column = index.column(); + return new QLineEdit(parent); +} - if ( column == SALTNAMECOL || column == SALTADDTOCOL ) { +void SaltItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { + auto const columnIndex = static_cast(index.column()); + if (columnIndex == SaltTableModel::ColumnIndex::Name || + columnIndex == SaltTableModel::ColumnIndex::AddTo) { QComboBox *box = qobject_cast(editor); box->setCurrentIndex(index.model()->data(index,Qt::UserRole).toInt()); - } - else { + } else { QLineEdit* line = qobject_cast(editor); line->setText(index.model()->data(index, Qt::DisplayRole).toString()); } + return; } -void SaltItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const -{ - int column = index.column(); - - if ( column == SALTNAMECOL || column == SALTADDTOCOL ) { +void SaltItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { + auto const columnIndex = static_cast(index.column()); + if (columnIndex == SaltTableModel::ColumnIndex::Name || + columnIndex == SaltTableModel::ColumnIndex::AddTo) { QComboBox* box = static_cast(editor); int selected = box->currentData().toInt(); int stored = model->data(index,Qt::UserRole).toInt(); @@ -612,21 +620,24 @@ void SaltItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, if ( selected != stored ) { model->setData(index,selected,Qt::EditRole); } - } - else { + } else { QLineEdit* line = static_cast(editor); - if ( line->isModified() ) + if ( line->isModified() ) { model->setData(index, line->text(), Qt::EditRole); + } } + return; } -void SaltItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex& /*index*/) const -{ +void SaltItemDelegate::updateEditorGeometry(QWidget * editor, + const QStyleOptionViewItem & option, + const QModelIndex & /*index*/) const { editor->setGeometry(option.rect); + return; } -void SaltItemDelegate::observeRecipe( Recipe* rec ) -{ +void SaltItemDelegate::observeRecipe( Recipe* rec ) { m_mash = rec->mash(); + return; } diff --git a/src/tableModels/SaltTableModel.h b/src/tableModels/SaltTableModel.h index db43c32d..6b018932 100644 --- a/src/tableModels/SaltTableModel.h +++ b/src/tableModels/SaltTableModel.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * tableModels/SaltTableModel.h is part of Brewken, and is copyright the following authors 2009-2022: + * tableModels/SaltTableModel.h is part of Brewken, and is copyright the following authors 2009-2023: * • Jeff Bailey * • Matt Young * • Mik Firestone @@ -39,11 +39,6 @@ class Recipe; class SaltItemDelegate; class WaterDialog; -enum{ SALTNAMECOL, - SALTAMOUNTCOL, - SALTADDTOCOL, - SALTPCTACIDCOL, - SALTNUMCOLS /*This one MUST be last*/}; /*! * \class SaltTableModel * @@ -53,8 +48,19 @@ class SaltTableModel : public BtTableModelRecipeObserver, public BtTableModelDat Q_OBJECT public: + enum class ColumnIndex { + Name , + Amount , + AddTo , + PctAcid, + }; + SaltTableModel(QTableView* parent = nullptr); ~SaltTableModel(); + + //! \brief Casting wrapper for \c BtTableModel::getColumnInfo + ColumnInfo const & getColumnInfo(ColumnIndex const columnIndex) const; + void observeRecipe(Recipe* rec); private: void addSalt(std::shared_ptr salt); diff --git a/src/tableModels/WaterTableModel.cpp b/src/tableModels/WaterTableModel.cpp index ce035739..22e7a5d8 100644 --- a/src/tableModels/WaterTableModel.cpp +++ b/src/tableModels/WaterTableModel.cpp @@ -43,14 +43,16 @@ WaterTableModel::WaterTableModel(WaterTableWidget * parent) : BtTableModelRecipeObserver{ parent, false, - {{WATERNAMECOL, {tr("Name"), NonPhysicalQuantity::String , "" }}, - {WATERAMOUNTCOL, {tr("Amount"), Measurement::PhysicalQuantity::Volume, *PropertyNames::Water::amount}}, - {WATERCALCIUMCOL, {tr("Calcium (ppm)"), NonPhysicalQuantity::Count , "" }}, - {WATERBICARBONATECOL, {tr("Bicarbonate (ppm)"), NonPhysicalQuantity::Count , "" }}, - {WATERSULFATECOL, {tr("Sulfate (ppm)"), NonPhysicalQuantity::Count , "" }}, - {WATERCHLORIDECOL, {tr("Chloride (ppm)"), NonPhysicalQuantity::Count , "" }}, - {WATERSODIUMCOL, {tr("Sodium (ppm)"), NonPhysicalQuantity::Count , "" }}, - {WATERMAGNESIUMCOL, {tr("Magnesium (ppm)"), NonPhysicalQuantity::Count , "" }}} + { + SMART_COLUMN_HEADER_DEFN(WaterTableModel, Name , tr("Name" ), NonPhysicalQuantity::String ), + SMART_COLUMN_HEADER_DEFN(WaterTableModel, Amount , tr("Amount" ), Measurement::PhysicalQuantity::Volume ), + SMART_COLUMN_HEADER_DEFN(WaterTableModel, Calcium , tr("Calcium (ppm)" ), Measurement::PhysicalQuantity::VolumeConcentration), + SMART_COLUMN_HEADER_DEFN(WaterTableModel, Bicarbonate, tr("Bicarbonate (ppm)"), Measurement::PhysicalQuantity::VolumeConcentration), + SMART_COLUMN_HEADER_DEFN(WaterTableModel, Sulfate , tr("Sulfate (ppm)" ), Measurement::PhysicalQuantity::VolumeConcentration), + SMART_COLUMN_HEADER_DEFN(WaterTableModel, Chloride , tr("Chloride (ppm)" ), Measurement::PhysicalQuantity::VolumeConcentration), + SMART_COLUMN_HEADER_DEFN(WaterTableModel, Sodium , tr("Sodium (ppm)" ), Measurement::PhysicalQuantity::VolumeConcentration), + SMART_COLUMN_HEADER_DEFN(WaterTableModel, Magnesium , tr("Magnesium (ppm)" ), Measurement::PhysicalQuantity::VolumeConcentration), + } }, BtTableModelData{} { return; @@ -58,6 +60,10 @@ WaterTableModel::WaterTableModel(WaterTableWidget * parent) : WaterTableModel::~WaterTableModel() = default; +BtTableModel::ColumnInfo const & WaterTableModel::getColumnInfo(WaterTableModel::ColumnIndex const columnIndex) const { + return this->BtTableModel::getColumnInfo(static_cast(columnIndex)); +} + void WaterTableModel::observeRecipe(Recipe * rec) { if (recObs) { disconnect(recObs, nullptr, this, nullptr); @@ -69,6 +75,7 @@ void WaterTableModel::observeRecipe(Recipe * rec) { connect(recObs, &NamedEntity::changed, this, &WaterTableModel::changed); addWaters(recObs->getAll()); } + return; } void WaterTableModel::observeDatabase(bool val) { @@ -181,7 +188,7 @@ void WaterTableModel::changed(QMetaProperty prop, QVariant val) { int ii = findIndexOf(waterSender); if (ii >= 0) { emit dataChanged(QAbstractItemModel::createIndex(ii, 0), - QAbstractItemModel::createIndex(ii, WATERNUMCOLS - 1)); + QAbstractItemModel::createIndex(ii, this->columnCount() - 1)); } } return; @@ -205,45 +212,43 @@ QVariant WaterTableModel::data(const QModelIndex & index, int role) const { return QVariant(); } - switch (index.column()) { - case WATERNAMECOL: + auto const columnIndex = static_cast(index.column()); + switch (columnIndex) { + case WaterTableModel::ColumnIndex::Name: return QVariant(row->name()); - case WATERAMOUNTCOL: + case WaterTableModel::ColumnIndex::Amount: return QVariant(Measurement::displayAmount(Measurement::Amount{row->amount(), Measurement::Units::liters})); - case WATERCALCIUMCOL: + case WaterTableModel::ColumnIndex::Calcium: return QVariant(Measurement::displayQuantity(row->calcium_ppm(), 3)); - case WATERBICARBONATECOL: + case WaterTableModel::ColumnIndex::Bicarbonate: return QVariant(Measurement::displayQuantity(row->bicarbonate_ppm(), 3)); - case WATERSULFATECOL: + case WaterTableModel::ColumnIndex::Sulfate: return QVariant(Measurement::displayQuantity(row->sulfate_ppm(), 3)); - case WATERCHLORIDECOL: + case WaterTableModel::ColumnIndex::Chloride: return QVariant(Measurement::displayQuantity(row->chloride_ppm(), 3)); - case WATERSODIUMCOL: + case WaterTableModel::ColumnIndex::Sodium: return QVariant(Measurement::displayQuantity(row->sodium_ppm(), 3)); - case WATERMAGNESIUMCOL: + case WaterTableModel::ColumnIndex::Magnesium: return QVariant(Measurement::displayQuantity(row->magnesium_ppm(), 3)); default : - qWarning() << tr("Bad column: %1").arg(index.column()); + qWarning() << Q_FUNC_INFO << tr("Bad column: %1").arg(index.column()); return QVariant(); } } QVariant WaterTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { - return this->getColumName(section); + return this->getColumnLabel(section); } return QVariant(); } Qt::ItemFlags WaterTableModel::flags(const QModelIndex & index) const { - int col = index.column(); - switch (col) { - case WATERNAMECOL: - return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; - default: - return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | - Qt::ItemIsEnabled; + auto const columnIndex = static_cast(index.column()); + if (columnIndex == WaterTableModel::ColumnIndex::Name) { + return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; } + return Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; } bool WaterTableModel::setData(QModelIndex const & index, QVariant const & value, int role) { @@ -258,38 +263,38 @@ bool WaterTableModel::setData(QModelIndex const & index, QVariant const & value, auto row = this->rows[index.row()]; - int const column = index.column(); - switch (column) { - case WATERNAMECOL: + auto const columnIndex = static_cast(index.column()); + switch (columnIndex) { + case WaterTableModel::ColumnIndex::Name: row->setName(value.toString()); break; - case WATERAMOUNTCOL: + case WaterTableModel::ColumnIndex::Amount: row->setAmount(Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Volume, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity()); + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale()).quantity()); break; - case WATERCALCIUMCOL: + case WaterTableModel::ColumnIndex::Calcium: row->setCalcium_ppm(Localization::toDouble(value.toString(), Q_FUNC_INFO)); break; - case WATERBICARBONATECOL: + case WaterTableModel::ColumnIndex::Bicarbonate: row->setBicarbonate_ppm(Localization::toDouble(value.toString(), Q_FUNC_INFO)); break; - case WATERSULFATECOL: + case WaterTableModel::ColumnIndex::Sulfate: row->setSulfate_ppm(Localization::toDouble(value.toString(), Q_FUNC_INFO)); break; - case WATERCHLORIDECOL: + case WaterTableModel::ColumnIndex::Chloride: row->setChloride_ppm(Localization::toDouble(value.toString(), Q_FUNC_INFO)); break; - case WATERSODIUMCOL: + case WaterTableModel::ColumnIndex::Sodium: row->setSodium_ppm(Localization::toDouble(value.toString(), Q_FUNC_INFO)); break; - case WATERMAGNESIUMCOL: + case WaterTableModel::ColumnIndex::Magnesium: row->setMagnesium_ppm(Localization::toDouble(value.toString(), Q_FUNC_INFO)); break; default: retval = false; - qWarning() << Q_FUNC_INFO << "Bad column: " << column; + qWarning() << Q_FUNC_INFO << "Bad column: " << index.column(); break; } @@ -300,6 +305,7 @@ bool WaterTableModel::setData(QModelIndex const & index, QVariant const & value, WaterItemDelegate::WaterItemDelegate(QObject * parent) : QItemDelegate(parent) { + return; } QWidget * WaterItemDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & /*option*/, @@ -310,6 +316,7 @@ QWidget * WaterItemDelegate::createEditor(QWidget * parent, const QStyleOptionVi void WaterItemDelegate::setEditorData(QWidget * editor, const QModelIndex & index) const { QLineEdit * line = qobject_cast(editor); line->setText(index.model()->data(index, Qt::DisplayRole).toString()); + return; } void WaterItemDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const { @@ -318,9 +325,11 @@ void WaterItemDelegate::setModelData(QWidget * editor, QAbstractItemModel * mode if (line->isModified()) { model->setData(index, line->text(), Qt::EditRole); } + return; } void WaterItemDelegate::updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & /*index*/) const { editor->setGeometry(option.rect); + return; } diff --git a/src/tableModels/WaterTableModel.h b/src/tableModels/WaterTableModel.h index 2fc39672..1e97ab42 100644 --- a/src/tableModels/WaterTableModel.h +++ b/src/tableModels/WaterTableModel.h @@ -40,10 +40,6 @@ class Recipe; class WaterItemDelegate; -enum{ WATERNAMECOL, WATERAMOUNTCOL, WATERCALCIUMCOL, WATERBICARBONATECOL, - WATERSULFATECOL, WATERCHLORIDECOL, WATERSODIUMCOL, WATERMAGNESIUMCOL, - WATERNUMCOLS /*This one MUST be last*/}; - /*! * \class WaterTableModel * @@ -53,9 +49,22 @@ class WaterTableModel : public BtTableModelRecipeObserver, public BtTableModelDa Q_OBJECT public: + enum class ColumnIndex { + Name , + Amount , + Calcium , + Bicarbonate, + Sulfate , + Chloride , + Sodium , + Magnesium , + }; WaterTableModel(WaterTableWidget* parent = nullptr); virtual ~WaterTableModel(); + //! \brief Casting wrapper for \c BtTableModel::getColumnInfo + ColumnInfo const & getColumnInfo(ColumnIndex const columnIndex) const; + void addWaters(QList > waters); void observeRecipe(Recipe* rec); void observeDatabase(bool val); diff --git a/src/tableModels/YeastTableModel.cpp b/src/tableModels/YeastTableModel.cpp index 649ea9ab..66ac385c 100644 --- a/src/tableModels/YeastTableModel.cpp +++ b/src/tableModels/YeastTableModel.cpp @@ -49,13 +49,15 @@ YeastTableModel::YeastTableModel(QTableView * parent, bool editable) : BtTableModelInventory{ parent, editable, - {{YEASTNAMECOL, {tr("Name"), NonPhysicalQuantity::String , "" }}, - {YEASTLABCOL, {tr("Laboratory"), NonPhysicalQuantity::String , "" }}, - {YEASTPRODIDCOL, {tr("Product ID"), NonPhysicalQuantity::String , "" }}, - {YEASTTYPECOL, {tr("Type"), NonPhysicalQuantity::String , "" }}, - {YEASTFORMCOL, {tr("Form"), NonPhysicalQuantity::String , "" }}, - {YEASTAMOUNTCOL, {tr("Amount"), Measurement::PqEitherMassOrVolume, *PropertyNames::Yeast::amount}}, - {YEASTINVENTORYCOL, {tr("Inventory"), NonPhysicalQuantity::Count , "" }}} + { + SMART_COLUMN_HEADER_DEFN(YeastTableModel, Name , tr("Name" ), NonPhysicalQuantity::String ), + SMART_COLUMN_HEADER_DEFN(YeastTableModel, Lab , tr("Laboratory"), NonPhysicalQuantity::String ), + SMART_COLUMN_HEADER_DEFN(YeastTableModel, ProdId , tr("Product ID"), NonPhysicalQuantity::String ), + SMART_COLUMN_HEADER_DEFN(YeastTableModel, Type , tr("Type" ), NonPhysicalQuantity::String ), + SMART_COLUMN_HEADER_DEFN(YeastTableModel, Form , tr("Form" ), NonPhysicalQuantity::String ), + SMART_COLUMN_HEADER_DEFN(YeastTableModel, Amount , tr("Amount" ), Measurement::PqEitherMassOrVolume), + SMART_COLUMN_HEADER_DEFN(YeastTableModel, Inventory, tr("Inventory" ), NonPhysicalQuantity::Count ), + } }, BtTableModelData{} { @@ -75,6 +77,10 @@ YeastTableModel::YeastTableModel(QTableView * parent, bool editable) : YeastTableModel::~YeastTableModel() = default; +BtTableModel::ColumnInfo const & YeastTableModel::getColumnInfo(YeastTableModel::ColumnIndex const columnIndex) const { + return this->BtTableModel::getColumnInfo(static_cast(columnIndex)); +} + void YeastTableModel::addYeast(int yeastId) { auto yeast = ObjectStoreWrapper::getById(yeastId); @@ -193,8 +199,8 @@ void YeastTableModel::changedInventory(int invKey, BtStringConst const & propert if (propertyName == PropertyNames::Inventory::amount) { for (int ii = 0; ii < this->rows.size(); ++ii) { if (invKey == this->rows.at(ii)->inventoryId()) { - emit dataChanged(QAbstractItemModel::createIndex(ii, YEASTINVENTORYCOL), - QAbstractItemModel::createIndex(ii, YEASTINVENTORYCOL)); + emit dataChanged(QAbstractItemModel::createIndex(ii, static_cast(YeastTableModel::ColumnIndex::Inventory)), + QAbstractItemModel::createIndex(ii, static_cast(YeastTableModel::ColumnIndex::Inventory))); } } } @@ -208,7 +214,7 @@ void YeastTableModel::changed(QMetaProperty prop, QVariant /*val*/) { int ii = this->findIndexOf(yeastSender); if (ii >= 0) { emit dataChanged(QAbstractItemModel::createIndex(ii, 0), - QAbstractItemModel::createIndex(ii, YEASTNUMCOLS - 1)); + QAbstractItemModel::createIndex(ii, this->columnCount() - 1)); } return; } @@ -240,14 +246,14 @@ QVariant YeastTableModel::data(QModelIndex const & index, int role) const { auto row = this->rows[index.row()]; - int const column = index.column(); - switch (column) { - case YEASTNAMECOL: + auto const columnIndex = static_cast(index.column()); + switch (columnIndex) { + case YeastTableModel::ColumnIndex::Name: if (role == Qt::DisplayRole) { return QVariant(row->name()); } break; - case YEASTTYPECOL: + case YeastTableModel::ColumnIndex::Type: if (role == Qt::DisplayRole) { return QVariant(row->typeStringTr()); } @@ -255,17 +261,17 @@ QVariant YeastTableModel::data(QModelIndex const & index, int role) const { return QVariant(static_cast(row->type())); } break; - case YEASTLABCOL: + case YeastTableModel::ColumnIndex::Lab: if (role == Qt::DisplayRole) { return QVariant(row->laboratory()); } break; - case YEASTPRODIDCOL: + case YeastTableModel::ColumnIndex::ProdId: if (role == Qt::DisplayRole) { return QVariant(row->productID()); } break; - case YEASTFORMCOL: + case YeastTableModel::ColumnIndex::Form: if (role == Qt::DisplayRole) { return QVariant(row->formStringTr()); } @@ -273,12 +279,12 @@ QVariant YeastTableModel::data(QModelIndex const & index, int role) const { return QVariant(static_cast(row->form())); } break; - case YEASTINVENTORYCOL: + case YeastTableModel::ColumnIndex::Inventory: if (role == Qt::DisplayRole) { return QVariant(row->inventory()); } break; - case YEASTAMOUNTCOL: + case YeastTableModel::ColumnIndex::Amount: if (role == Qt::DisplayRole) { return QVariant( Measurement::displayAmount( @@ -287,14 +293,14 @@ QVariant YeastTableModel::data(QModelIndex const & index, int role) const { row->amountIsWeight() ? Measurement::Units::kilograms : Measurement::Units::liters }, 3, - this->getForcedSystemOfMeasurementForColumn(column), + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), std::nullopt ) ); } break; default : - qWarning() << Q_FUNC_INFO << "Bad column: " << column; + qWarning() << Q_FUNC_INFO << "Bad column: " << index.column(); break; } return QVariant(); @@ -302,22 +308,21 @@ QVariant YeastTableModel::data(QModelIndex const & index, int role) const { QVariant YeastTableModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { - return this->getColumName(section); + return this->getColumnLabel(section); } return QVariant(); } Qt::ItemFlags YeastTableModel::flags(const QModelIndex & index) const { - int col = index.column(); - switch (col) { - case YEASTNAMECOL: - return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; - case YEASTINVENTORYCOL: - return (Qt::ItemIsEnabled | (this->isInventoryEditable() ? Qt::ItemIsEditable : Qt::NoItemFlags)); - default: - return Qt::ItemIsSelectable | (editable ? Qt::ItemIsEditable : Qt::NoItemFlags) | Qt::ItemIsDragEnabled | - Qt::ItemIsEnabled; + auto const columnIndex = static_cast(index.column()); + if (columnIndex == YeastTableModel::ColumnIndex::Name) { + return Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; + } + if (columnIndex == YeastTableModel::ColumnIndex::Inventory) { + return (Qt::ItemIsEnabled | (this->isInventoryEditable() ? Qt::ItemIsEditable : Qt::NoItemFlags)); } + return Qt::ItemIsSelectable | + (this->editable ? Qt::ItemIsEditable : Qt::NoItemFlags) | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled; } bool YeastTableModel::setData(QModelIndex const & index, QVariant const & value, int role) { @@ -327,9 +332,9 @@ bool YeastTableModel::setData(QModelIndex const & index, QVariant const & value, auto row = this->rows[index.row()]; - int const column = index.column(); - switch (column) { - case YEASTNAMECOL: + auto const columnIndex = static_cast(index.column()); + switch (columnIndex) { + case YeastTableModel::ColumnIndex::Name: if (!value.canConvert(QVariant::String)) { return false; } @@ -338,7 +343,7 @@ bool YeastTableModel::setData(QModelIndex const & index, QVariant const & value, value.toString(), tr("Change Yeast Name")); break; - case YEASTLABCOL: + case YeastTableModel::ColumnIndex::Lab: if (!value.canConvert(QVariant::String)) { return false; } @@ -347,7 +352,7 @@ bool YeastTableModel::setData(QModelIndex const & index, QVariant const & value, value.toString(), tr("Change Yeast Laboratory")); break; - case YEASTPRODIDCOL: + case YeastTableModel::ColumnIndex::ProdId: if (!value.canConvert(QVariant::String)) { return false; } @@ -356,7 +361,7 @@ bool YeastTableModel::setData(QModelIndex const & index, QVariant const & value, value.toString(), tr("Change Yeast Product ID")); break; - case YEASTTYPECOL: + case YeastTableModel::ColumnIndex::Type: if (!value.canConvert(QVariant::Int)) { return false; } @@ -365,7 +370,7 @@ bool YeastTableModel::setData(QModelIndex const & index, QVariant const & value, value.toInt(), tr("Change Yeast Type")); break; - case YEASTFORMCOL: + case YeastTableModel::ColumnIndex::Form: if (!value.canConvert(QVariant::Int)) { return false; } @@ -374,7 +379,7 @@ bool YeastTableModel::setData(QModelIndex const & index, QVariant const & value, value.toInt(), tr("Change Yeast Form")); break; - case YEASTINVENTORYCOL: + case YeastTableModel::ColumnIndex::Inventory: if (!value.canConvert(QVariant::Int)) { return false; } @@ -383,7 +388,7 @@ bool YeastTableModel::setData(QModelIndex const & index, QVariant const & value, value.toInt(), tr("Change Yeast Inventory Unit Size")); break; - case YEASTAMOUNTCOL: + case YeastTableModel::ColumnIndex::Amount: if (!value.canConvert(QVariant::String)) { return false; } @@ -394,14 +399,14 @@ bool YeastTableModel::setData(QModelIndex const & index, QVariant const & value, Measurement::qStringToSI( value.toString(), row->amountIsWeight() ? Measurement::PhysicalQuantity::Mass : Measurement::PhysicalQuantity::Volume, - this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column) + this->getColumnInfo(columnIndex).getForcedSystemOfMeasurement(), + this->getColumnInfo(columnIndex).getForcedRelativeScale() ).quantity(), tr("Change Yeast Amount") ); break; default: - qWarning() << Q_FUNC_INFO << "Bad column: " << column; + qWarning() << Q_FUNC_INFO << "Bad column: " << index.column(); return false; } return true; @@ -415,11 +420,9 @@ YeastItemDelegate::YeastItemDelegate(QObject * parent) QWidget * YeastItemDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & /*option*/, const QModelIndex & index) const { - int col = index.column(); - - if (col == YEASTTYPECOL) { + auto const columnIndex = static_cast(index.column()); + if (columnIndex == YeastTableModel::ColumnIndex::Type) { QComboBox * box = new QComboBox(parent); - box->addItem(tr("Ale")); box->addItem(tr("Lager")); box->addItem(tr("Wheat")); @@ -427,11 +430,11 @@ QWidget * YeastItemDelegate::createEditor(QWidget * parent, const QStyleOptionVi box->addItem(tr("Champagne")); box->setMinimumWidth(box->minimumSizeHint().width()); box->setSizeAdjustPolicy(QComboBox::AdjustToContents); - return box; - } else if (col == YEASTFORMCOL) { - QComboBox * box = new QComboBox(parent); + } + if (columnIndex == YeastTableModel::ColumnIndex::Form) { + QComboBox * box = new QComboBox(parent); box->addItem(tr("Liquid")); box->addItem(tr("Dry")); box->addItem(tr("Slant")); @@ -439,15 +442,15 @@ QWidget * YeastItemDelegate::createEditor(QWidget * parent, const QStyleOptionVi box->setMinimumWidth(box->minimumSizeHint().width()); box->setSizeAdjustPolicy(QComboBox::AdjustToContents); return box; - } else { - return new QLineEdit(parent); } + + return new QLineEdit(parent); } void YeastItemDelegate::setEditorData(QWidget * editor, const QModelIndex & index) const { - int col = index.column(); - - if (col == YEASTTYPECOL || col == YEASTFORMCOL) { + auto const columnIndex = static_cast(index.column()); + if (columnIndex == YeastTableModel::ColumnIndex::Type || + columnIndex == YeastTableModel::ColumnIndex::Form) { QComboBox * box = qobject_cast(editor); int ndx = index.model()->data(index, Qt::UserRole).toInt(); @@ -458,12 +461,13 @@ void YeastItemDelegate::setEditorData(QWidget * editor, const QModelIndex & inde line->setText(index.model()->data(index, Qt::DisplayRole).toString()); } + return; } void YeastItemDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const { - int col = index.column(); - - if (col == YEASTTYPECOL || col == YEASTFORMCOL) { + auto const columnIndex = static_cast(index.column()); + if (columnIndex == YeastTableModel::ColumnIndex::Type || + columnIndex == YeastTableModel::ColumnIndex::Form) { QComboBox * box = static_cast(editor); int ndx = box->currentIndex(); int curr = model->data(index, Qt::UserRole).toInt(); @@ -478,9 +482,11 @@ void YeastItemDelegate::setModelData(QWidget * editor, QAbstractItemModel * mode model->setData(index, line->text(), Qt::EditRole); } } + return; } void YeastItemDelegate::updateEditorGeometry(QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & /*index*/) const { editor->setGeometry(option.rect); + return; } diff --git a/src/tableModels/YeastTableModel.h b/src/tableModels/YeastTableModel.h index d41eec54..d400e461 100644 --- a/src/tableModels/YeastTableModel.h +++ b/src/tableModels/YeastTableModel.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * tableModels/YeastTableModel.h is part of Brewken, and is copyright the following authors 2009-2022: + * tableModels/YeastTableModel.h is part of Brewken, and is copyright the following authors 2009-2023: * • Jeff Bailey * • Matt Young * • Mik Firestone @@ -41,8 +41,6 @@ class YeastTableWidget; class YeastItemDelegate; class Recipe; -enum{ YEASTNAMECOL, YEASTLABCOL, YEASTPRODIDCOL, YEASTTYPECOL, YEASTFORMCOL, YEASTAMOUNTCOL, YEASTINVENTORYCOL, YEASTNUMCOLS /*This one MUST be last*/}; - /*! * \class YeastTableModel * @@ -52,9 +50,21 @@ class YeastTableModel : public BtTableModelInventory, public BtTableModelData - * • Matt Young - * • Mik Firestone - * • Philip Greggory Lee - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "widgets/BtAmountDigitWidget.h" - -BtAmountDigitWidget::BtAmountDigitWidget(QWidget * parent, - Measurement::PhysicalQuantities const physicalQuantities) : - BtDigitWidget{parent, ConvertToBtFieldType(physicalQuantities)}, - UiAmountWithUnits(parent, physicalQuantities) { - return; -} - -BtAmountDigitWidget::~BtAmountDigitWidget() = default; - -void BtAmountDigitWidget::displayChanged(PreviousScaleInfo previousScaleInfo) { - this->QLabel::setText(this->correctEnteredText(this->text(), this->getPrecision(), previousScaleInfo)); - return; -} - -BtMassDigit::BtMassDigit(QWidget* parent) : - BtAmountDigitWidget{parent, Measurement::PhysicalQuantity::Mass} { - return; -} diff --git a/src/widgets/BtAmountDigitWidget.h b/src/widgets/BtAmountDigitWidget.h deleted file mode 100644 index f8961e51..00000000 --- a/src/widgets/BtAmountDigitWidget.h +++ /dev/null @@ -1,60 +0,0 @@ -/*====================================================================================================================== - * widgets/BtAmountDigitWidget.h is part of Brewken, and is copyright the following authors 2009-2023: - * • Matt Young - * • Mik Firestone - * • Philip Greggory Lee - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#ifndef WIDGETS_BTAMOUNTDIGITWIDGET_H -#define WIDGETS_BTAMOUNTDIGITWIDGET_H -#pragma once - -#include "widgets/BtDigitWidget.h" - -/** - * \brief Extends \c BtDigitWidget to show units - * - * NB: Per https://doc.qt.io/qt-5/moc.html#multiple-inheritance-requires-qobject-to-be-first, "If you are using - * multiple inheritance, moc [Qt's Meta-Object Compiler] assumes that the first inherited class is a subclass of - * QObject. Also, be sure that only the first inherited class is a QObject." In particular, this means we must - * put Q_PROPERTY declarations for UiAmountWithUnits attributes here rather than in UiAmountWithUnits itself. - */ -class BtAmountDigitWidget : public BtDigitWidget, public UiAmountWithUnits { - Q_OBJECT -public: -/// Q_PROPERTY(int type READ type WRITE setType STORED false) - Q_PROPERTY(QString configSection READ getConfigSection WRITE setConfigSection STORED false) - Q_PROPERTY(QString editField READ getEditField WRITE setEditField STORED false) - Q_PROPERTY(QString forcedSystemOfMeasurement READ getForcedSystemOfMeasurementViaString WRITE setForcedSystemOfMeasurementViaString STORED false) - Q_PROPERTY(QString forcedRelativeScale READ getForcedRelativeScaleViaString WRITE setForcedRelativeScaleViaString STORED false) - - BtAmountDigitWidget(QWidget * parent, - Measurement::PhysicalQuantities const physicalQuantities); - virtual ~BtAmountDigitWidget(); - -public slots: - /** - * \brief Received from \c BtLabel when the user has change \c UnitSystem - * - * This is mostly referenced in .ui files. (NB this means that the signal connections are only checked at run-time.) - */ - void displayChanged(PreviousScaleInfo previousScaleInfo); - -}; - -// -// See comment in BtLineEdit.h for why we need these trivial child classes to use in .ui files -// -class BtMassDigit : public BtAmountDigitWidget { Q_OBJECT public: BtMassDigit(QWidget * parent); }; - -#endif diff --git a/src/widgets/BtDigitWidget.cpp b/src/widgets/SmartDigitWidget.cpp similarity index 72% rename from src/widgets/BtDigitWidget.cpp rename to src/widgets/SmartDigitWidget.cpp index 77297599..66aeb533 100644 --- a/src/widgets/BtDigitWidget.cpp +++ b/src/widgets/SmartDigitWidget.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * widgets/BtDigitWidget.cpp is part of Brewken, and is copyright the following authors 2009-2023: + * widgets/SmartDigitWidget.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Mattias Måhl * • Matt Young * • Mik Firestone @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#include "widgets/BtDigitWidget.h" +#include "widgets/SmartDigitWidget.h" #include @@ -29,14 +29,15 @@ #include "measurement/Unit.h" #include "measurement/UnitSystem.h" #include "PersistentSettings.h" +#include "widgets/SmartLabel.h" -// This private implementation class holds all private non-virtual members of BtDigitWidget -class BtDigitWidget::impl { +// This private implementation class holds all private non-virtual members of SmartDigitWidget +class SmartDigitWidget::impl { public: /** * Constructor */ - impl(BtDigitWidget & self) : + impl(SmartDigitWidget & self) : self {self}, m_rgblow {0x0000d0}, m_rgbgood {0x008000}, @@ -47,9 +48,9 @@ class BtDigitWidget::impl { m_constantColor{false}, m_lastNum {1.5}, m_lastPrec {3}, - m_low_msg {BtDigitWidget::tr("Too low for style.")}, - m_good_msg {BtDigitWidget::tr("In range for style.")}, - m_high_msg {BtDigitWidget::tr("Too high for style.")} { + m_low_msg {SmartDigitWidget::tr("Too low for style.")}, + m_good_msg {SmartDigitWidget::tr("In range for style.")}, + m_high_msg {SmartDigitWidget::tr("Too high for style.")} { this->self.setStyleSheet(m_styleSheet.arg(0,6,16,QChar('0'))); this->self.setFrameStyle(QFrame::Box); this->self.setFrameShadow(QFrame::Sunken); @@ -91,7 +92,7 @@ class BtDigitWidget::impl { } // Member variables for impl - BtDigitWidget & self; + SmartDigitWidget & self; unsigned int m_rgblow; unsigned int m_rgbgood; unsigned int m_rgbhigh; @@ -107,17 +108,34 @@ class BtDigitWidget::impl { QString m_high_msg; }; -BtDigitWidget::BtDigitWidget(QWidget *parent, - BtFieldType fieldType) : +SmartDigitWidget::SmartDigitWidget(QWidget *parent) : QLabel(parent), - fieldType{fieldType}, + SmartField{}, pimpl{std::make_unique(*this)} { return; } -BtDigitWidget::~BtDigitWidget() = default; +SmartDigitWidget::~SmartDigitWidget() = default; -void BtDigitWidget::display(QString str) { +QString SmartDigitWidget::getRawText() const { + return this->text(); +} + +void SmartDigitWidget::setRawText(QString const & text) { + this->QLabel::setText(text); + return; +} + +void SmartDigitWidget::connectSmartLabelSignal(SmartLabel & smartLabel) { + connect(&smartLabel, &SmartLabel::changedSystemOfMeasurementOrScale, this, &SmartDigitWidget::displayChanged); + return; +} + +void SmartDigitWidget::doPostInitWork() { + return; +} + +void SmartDigitWidget::display(QString str) { static bool converted; this->pimpl->m_lastNum = Localization::toDouble(str, &converted); @@ -131,7 +149,7 @@ void BtDigitWidget::display(QString str) { return; } -void BtDigitWidget::display(double num, int prec) { +void SmartDigitWidget::display(double num, int prec) { this->pimpl->m_lastNum = num; this->pimpl->m_lastPrec = prec; @@ -139,7 +157,7 @@ void BtDigitWidget::display(double num, int prec) { return; } -void BtDigitWidget::setLowLim(double num) { +void SmartDigitWidget::setLowLim(double num) { if (num < this->pimpl->m_highLim) { this->pimpl->m_lowLim = num; } @@ -147,7 +165,7 @@ void BtDigitWidget::setLowLim(double num) { return; } -void BtDigitWidget::setHighLim(double num) { +void SmartDigitWidget::setHighLim(double num) { if (num > this->pimpl->m_lowLim) { this->pimpl->m_highLim = num; } @@ -155,14 +173,14 @@ void BtDigitWidget::setHighLim(double num) { return; } -void BtDigitWidget::setConstantColor(ColorType c) { +void SmartDigitWidget::setConstantColor(ColorType c) { this->pimpl->m_constantColor = (c == LOW || c == GOOD || c == HIGH || c == BLACK ); this->pimpl->m_color = c; this->update(); // repaint. return; } -void BtDigitWidget::setLimits(double low, double high) { +void SmartDigitWidget::setLimits(double low, double high) { if (low < high) { this->pimpl->m_lowLim = low; this->pimpl->m_highLim = high; @@ -172,11 +190,11 @@ void BtDigitWidget::setLimits(double low, double high) { return; } -void BtDigitWidget::setLowMsg (QString msg) { this->pimpl->m_low_msg = msg; this->update(); return; } -void BtDigitWidget::setGoodMsg(QString msg) { this->pimpl->m_good_msg = msg; this->update(); return; } -void BtDigitWidget::setHighMsg(QString msg) { this->pimpl->m_high_msg = msg; this->update(); return; } +void SmartDigitWidget::setLowMsg (QString msg) { this->pimpl->m_low_msg = msg; this->update(); return; } +void SmartDigitWidget::setGoodMsg(QString msg) { this->pimpl->m_good_msg = msg; this->update(); return; } +void SmartDigitWidget::setHighMsg(QString msg) { this->pimpl->m_high_msg = msg; this->update(); return; } -void BtDigitWidget::setMessages( QStringList msgs ) { +void SmartDigitWidget::setMessages( QStringList msgs ) { if ( msgs.size() != 3 ) { qWarning() << Q_FUNC_INFO << "Wrong number of messages"; return; @@ -189,7 +207,7 @@ void BtDigitWidget::setMessages( QStringList msgs ) { return; } -void BtDigitWidget::setText(QString amount, int precision) { +void SmartDigitWidget::setText(QString amount, int precision) { if (NonPhysicalQuantity::String != std::get(this->fieldType)) { bool ok = false; double amt = Measurement::extractRawFromString(amount, &ok); @@ -206,7 +224,7 @@ void BtDigitWidget::setText(QString amount, int precision) { return; } -void BtDigitWidget::setText(double amount, int precision) { +void SmartDigitWidget::setText(double amount, int precision) { this->pimpl->m_lastNum = amount; this->pimpl->m_lastPrec = precision; // this->setConfigSection(""); @@ -214,7 +232,7 @@ void BtDigitWidget::setText(double amount, int precision) { return; } -template T BtDigitWidget::getValueAs() const { +template T SmartDigitWidget::getValueAs() const { return Measurement::extractRawFromString(this->text()); } // @@ -222,15 +240,16 @@ template T BtDigitWidget::getValueAs() const { // (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.) // -template int BtDigitWidget::getValueAs() const; -template unsigned int BtDigitWidget::getValueAs() const; -template double BtDigitWidget::getValueAs() const; +template int SmartDigitWidget::getValueAs() const; +template unsigned int SmartDigitWidget::getValueAs() const; +template double SmartDigitWidget::getValueAs() const; -int BtDigitWidget::getPrecision() const { +int SmartDigitWidget::getPrecision() const { return this->pimpl->m_lastPrec; } -BtGenericDigit::BtGenericDigit(QWidget* parent) : - BtDigitWidget{parent, NonPhysicalQuantity::Count} { + +void SmartDigitWidget::displayChanged(SmartAmounts::ScaleInfo previousScaleInfo) { + this->correctEnteredText(previousScaleInfo); return; } diff --git a/src/widgets/BtDigitWidget.h b/src/widgets/SmartDigitWidget.h similarity index 67% rename from src/widgets/BtDigitWidget.h rename to src/widgets/SmartDigitWidget.h index 966c0d64..43f8a911 100644 --- a/src/widgets/BtDigitWidget.h +++ b/src/widgets/SmartDigitWidget.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * widgets/BtDigitWidget.h is part of Brewken, and is copyright the following authors 2009-2023: + * widgets/SmartDigitWidget.h is part of Brewken, and is copyright the following authors 2009-2023: * • Matt Young * • Mik Firestone * • Philip Greggory Lee @@ -29,25 +29,34 @@ #include "measurement/PhysicalQuantity.h" #include "measurement/Unit.h" #include "measurement/UnitSystem.h" -#include "UiAmountWithUnits.h" +#include "SmartField.h" /*! - * \class BtDigitWidget + * \class SmartDigitWidget * * \brief Widget that displays colored numbers, depending on if the number is ok, high, or low. Currently only used in * waterDialog.ui (ie Water Chemistry Dialog). * * \todo Make this thing directly accept signals from the model items it is supposed to watch. + * + * NB: Per https://doc.qt.io/qt-5/moc.html#multiple-inheritance-requires-qobject-to-be-first, "If you are using + * multiple inheritance, moc [Qt's Meta-Object Compiler] assumes that the first inherited class is a subclass of + * QObject. Also, be sure that only the first inherited class is a QObject." In particular, this means we must + * put Q_PROPERTY declarations for SmartField attributes here rather than in SmartField itself. */ -class BtDigitWidget : public QLabel { +class SmartDigitWidget : public QLabel, public SmartField { Q_OBJECT public: enum ColorType{NONE, LOW, GOOD, HIGH, BLACK}; - BtDigitWidget(QWidget * parent, - BtFieldType fieldType); - virtual ~BtDigitWidget(); + SmartDigitWidget(QWidget * parent); + virtual ~SmartDigitWidget(); + + virtual QString getRawText() const; + virtual void setRawText(QString const & text); + virtual void connectSmartLabelSignal(SmartLabel & smartLabel); + virtual void doPostInitWork(); //! \brief Displays the given \c num with precision \c prec. void display(double num, int prec = 0); @@ -86,6 +95,14 @@ class BtDigitWidget : public QLabel { */ template T getValueAs() const; +public slots: + /** + * \brief Received from \c SmartLabel when the user has change \c UnitSystem + * + * This is mostly referenced in .ui files. (NB this means that the signal connections are only checked at run-time.) + */ + void displayChanged(SmartAmounts::ScaleInfo previousScaleInfo); + protected: int getPrecision() const; @@ -98,8 +115,8 @@ class BtDigitWidget : public QLabel { }; // -// See comment in BtLineEdit.h for why we need these trivial child classes to use in .ui files +// See comment in widgets/BtAmountDigitWidget.h for why we need these trivial child classes to use in .ui files // -class BtGenericDigit : public BtDigitWidget { Q_OBJECT public: BtGenericDigit(QWidget * parent); }; +class BtGenericDigit : public SmartDigitWidget { Q_OBJECT public: BtGenericDigit(QWidget * parent); }; #endif diff --git a/src/widgets/SmartLabel.cpp b/src/widgets/SmartLabel.cpp index 5ad0f693..9a03dd4d 100644 --- a/src/widgets/SmartLabel.cpp +++ b/src/widgets/SmartLabel.cpp @@ -22,14 +22,14 @@ #include #include #include +#include -#include "BtAmountEdit.h" #include "measurement/Measurement.h" #include "model/Style.h" #include "model/Recipe.h" #include "PersistentSettings.h" #include "utils/OptionalHelpers.h" -#include "widgets/SmartLineEdit.h" +#include "SmartField.h" #include "widgets/UnitAndScalePopUpMenu.h" // This private implementation class holds all private non-virtual members of SmartLabel @@ -37,40 +37,168 @@ class SmartLabel::impl { public: impl(SmartLabel & self, QWidget * parent) : - m_self {self }, - m_propertyName {"" }, - m_configSection{"" }, - m_parent {parent }, - m_contextMenu {nullptr} { + m_self {self }, + m_initialised {false}, + m_editorName {"Uninitialised m_editorName!" }, + m_labelName {"Uninitialised m_labelName!" }, + m_labelFqName {"Uninitialised m_labellFqName!"}, + m_typeInfo {nullptr}, + m_parent {parent }, + m_contextMenu {nullptr} { return; } ~impl() = default; - SmartLabel & m_self; - QString m_propertyName; - QString m_configSection; - QWidget * m_parent; - QMenu * m_contextMenu; + void initializeMenu() { + // TODO: Change this to a smart pointer + // + // If a context menu already exists, we need to delete it and recreate it. We can't always reuse an existing menu + // because the sub-menu for relative scale needs to change when a different unit system is selected. (In theory we + // could only recreate the context menu when a different unit system is selected, but that adds complication.) + if (this->m_contextMenu) { + // NB: Although the existing menu is "owned" by this->m_parent, it is fine for us to delete it here. The Qt + // ownership in this context merely guarantees that this->m_parent will, in its own destructor, delete the menu if + // it still exists. + delete this->m_contextMenu; + this->m_contextMenu = nullptr; + } + + auto forcedSystemOfMeasurement = this->m_self.getForcedSystemOfMeasurement(); + auto forcedRelativeScale = this->m_self.getForcedRelativeScale(); + qDebug() << + Q_FUNC_INFO << "forcedSystemOfMeasurement=" << forcedSystemOfMeasurement << ", forcedRelativeScale=" << + forcedRelativeScale; + + if (std::holds_alternative(*this->m_typeInfo->fieldType)) { + return; + } + + // Since fieldType is not NonPhysicalQuantity this cast should be safe + Measurement::PhysicalQuantity const physicalQuantity = + std::get(*this->m_typeInfo->fieldType); + this->m_contextMenu = UnitAndScalePopUpMenu::create(this->m_parent, + physicalQuantity, + forcedSystemOfMeasurement, + forcedRelativeScale); + return; + } + + + SmartLabel & m_self ; + bool m_initialised; + char const * m_editorName ; + char const * m_labelName ; + char const * m_labelFqName; + TypeInfo const * m_typeInfo ; + QWidget * m_parent ; + QMenu * m_contextMenu; }; SmartLabel::SmartLabel(QWidget * parent) : QLabel{parent}, - pimpl{std::make_unique(*this, parent)} { + pimpl {std::make_unique(*this, parent)} { connect(this, &QWidget::customContextMenuRequested, this, &SmartLabel::popContextMenu); return; } SmartLabel::~SmartLabel() = default; -SmartLineEdit & SmartLabel::getBuddy() const { - // Call QLabel's built-in function to get the buddy - QWidget * buddy = this->buddy(); +void SmartLabel::init(char const * const editorName, + char const * const labelName, + char const * const labelFqName, + SmartField * smartField, + TypeInfo const & typeInfo) { + this->pimpl->m_editorName = editorName ; + this->pimpl->m_labelName = labelName ; + this->pimpl->m_labelFqName = labelFqName; + this->pimpl->m_typeInfo = &typeInfo ; +/// if (smartField) { +/// this->pimpl->m_buddies.append(smartField); +/// this->setBuddy(smartField); +/// } + this->pimpl->m_initialised = true; + return; +} + +///SmartField & SmartLabel::getBuddy() const { +/// Q_ASSERT(this->pimpl->m_initialised); +/// +/// // Call QLabel's built-in function to get the buddy +/// QWidget * buddy = this->buddy(); +/// +/// // We assert that it's a coding error for there not to be a buddy! +/// Q_ASSERT(buddy); +/// +/// auto & smartBuddy = static_cast(*buddy); +/// +/// // We assert that the buddy was set via our init function. (It's OK if it was also set directly via the QLabel +/// // buddy property.) +/// Q_ASSERT(this->pimpl->m_buddies.contains(&smartBuddy)); +/// +/// return smartBuddy; +///} + +void SmartLabel::setForcedSystemOfMeasurement(std::optional systemOfMeasurement) { + SmartAmounts::setForcedSystemOfMeasurement(this->pimpl->m_editorName, this->pimpl->m_labelName, systemOfMeasurement); + return; +} - // We assert that it's a coding error for there not to be a buddy! - Q_ASSERT(buddy); +void SmartLabel::setForcedRelativeScale(std::optional relativeScale) { + SmartAmounts::setForcedRelativeScale(this->pimpl->m_editorName, this->pimpl->m_labelName, relativeScale); + return; +} - return static_cast(*buddy); +std::optional SmartLabel::getForcedSystemOfMeasurement() const { + return SmartAmounts::getForcedSystemOfMeasurement(this->pimpl->m_editorName, this->pimpl->m_labelName); +} + +std::optional SmartLabel::getForcedRelativeScale() const { + return SmartAmounts::getForcedRelativeScale(this->pimpl->m_editorName, this->pimpl->m_labelName); +} + +SmartAmounts::ScaleInfo SmartLabel::getScaleInfo() const { + Q_ASSERT(!std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)); + return SmartAmounts::getScaleInfo(this->pimpl->m_editorName, + this->pimpl->m_labelName, + ConvertToPhysicalQuantities(*this->pimpl->m_typeInfo->fieldType)); +} + +Measurement::UnitSystem const & SmartLabel::getDisplayUnitSystem() const { + // It's a coding error to call this for NonPhysicalQuantity, and we assert we never have a Mixed2PhysicalQuantities + // for a SmartLabel that has no associated SmartField. + Q_ASSERT(std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)); + return SmartAmounts::getUnitSystem(this->pimpl->m_editorName, + this->pimpl->m_labelName, + std::get(*this->pimpl->m_typeInfo->fieldType)); +} + +double SmartLabel::getAmountToDisplay(double const canonicalValue) const { + // It's a coding error to call this for NonPhysicalQuantity, and we assert we never have a Mixed2PhysicalQuantities + // for a SmartLabel that has no associated SmartField. + Q_ASSERT(std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)); + + auto const & canonicalUnit{ + Measurement::Unit::getCanonicalUnit(std::get(*this->pimpl->m_typeInfo->fieldType)) + }; + return Measurement::amountDisplay( + Measurement::Amount{canonicalValue, canonicalUnit}, + this->getForcedSystemOfMeasurement(), + this->getForcedRelativeScale() + ); +} + +QPair SmartLabel::getRangeToDisplay(double const canonicalValueMin, + double const canonicalValueMax) const { + auto const & canonicalUnit{ + Measurement::Unit::getCanonicalUnit(std::get(*this->pimpl->m_typeInfo->fieldType)) + }; + auto const forcedSystemOfMeasurement = this->getForcedSystemOfMeasurement(); + auto const forcedRelativeScale = this->getForcedRelativeScale(); + return QPair( + Measurement::amountDisplay(Measurement::Amount{canonicalValueMin, canonicalUnit}, forcedSystemOfMeasurement, forcedRelativeScale), + Measurement::amountDisplay(Measurement::Amount{canonicalValueMax, canonicalUnit}, forcedSystemOfMeasurement, forcedRelativeScale) + ); } void SmartLabel::enterEvent([[maybe_unused]] QEvent * event) { @@ -91,10 +219,11 @@ void SmartLabel::mouseReleaseEvent(QMouseEvent * event) { } void SmartLabel::textEffect(bool enabled) { - // If our buddy is an input field for a NonPhysicalQuantity, then we don't want the underline effect as there are no - // scale choices for the user to make. - auto const fieldType = this->getBuddy().getFieldType(); - if (std::holds_alternative(fieldType)) { + Q_ASSERT(this->pimpl->m_initialised); + + // If we are a label for a NonPhysicalQuantity, then we don't want the underline effect as there are no scale choices + // for the user to make. + if (std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)) { return; } @@ -104,101 +233,65 @@ void SmartLabel::textEffect(bool enabled) { return; } -void SmartLabel::initializeSection() { - if (!this->pimpl->m_configSection.isEmpty()) { - // We're already initialised - return; - } - - // - // If the label has the pimpl->m_configSection defined, use it - // otherwise, if the paired field has a pimpl->m_configSection, use it - // otherwise, if the parent object has a pimpl->m_configSection, use it - // if all else fails, get the parent's object name - // - if (this->property(*PropertyNames::UiAmountWithUnits::configSection).isValid()) { - this->pimpl->m_configSection = this->property(*PropertyNames::UiAmountWithUnits::configSection).toString(); - return; - } - - // As much as I dislike it, dynamic properties can't be referenced on initialization. - QWidget const * mybuddy = this->buddy(); - if (mybuddy && mybuddy->property(*PropertyNames::UiAmountWithUnits::configSection).isValid() ) { - this->pimpl->m_configSection = mybuddy->property(*PropertyNames::UiAmountWithUnits::configSection).toString(); - return; - } - - if (this->pimpl->m_parent->property(*PropertyNames::UiAmountWithUnits::configSection).isValid() ) { - this->pimpl->m_configSection = - this->pimpl->m_parent->property(*PropertyNames::UiAmountWithUnits::configSection).toString(); - return; - } - - qWarning() << Q_FUNC_INFO << "this failed" << this; - this->pimpl->m_configSection = this->pimpl->m_parent->objectName(); - return; -} - -void SmartLabel::initializeProperty() { - - if (!this->pimpl->m_propertyName.isEmpty()) { - return; - } - - QWidget* mybuddy = this->buddy(); - if (this->property("editField").isValid()) { - this->pimpl->m_propertyName = this->property("editField").toString(); - } else if (mybuddy && mybuddy->property("editField").isValid()) { - this->pimpl->m_propertyName = mybuddy->property("editField").toString(); - } else { - qWarning() << Q_FUNC_INFO << "That failed miserably"; - } - return; -} - -void SmartLabel::initializeMenu() { - // TODO: Change this to a smart pointer - // - // If a context menu already exists, we need to delete it and recreate it. We can't always reuse an existing menu - // because the sub-menu for relative scale needs to change when a different unit system is selected. (In theory we - // could only recreate the context menu when a different unit system is selected, but that adds complication.) - if (this->pimpl->m_contextMenu) { - // NB: Although the existing menu is "owned" by this->pimpl->m_parent, it is fine for us to delete it here. The Qt - // ownership in this context merely guarantees that this->pimpl->m_parent will, in its own destructor, delete the menu if - // it still exists. - delete this->pimpl->m_contextMenu; - this->pimpl->m_contextMenu = nullptr; - } - - std::optional forcedSystemOfMeasurement = - Measurement::getForcedSystemOfMeasurementForField(this->pimpl->m_propertyName, this->pimpl->m_configSection); - std::optional forcedRelativeScale = - Measurement::getForcedRelativeScaleForField(this->pimpl->m_propertyName, this->pimpl->m_configSection); - qDebug() << - Q_FUNC_INFO << "forcedSystemOfMeasurement=" << forcedSystemOfMeasurement << ", forcedRelativeScale=" << - forcedRelativeScale; - - auto const & buddy = this->getBuddy(); - auto fieldType = buddy.getFieldType(); - if (std::holds_alternative(fieldType)) { - return; - } - - // Since fieldType is not NonPhysicalQuantity this cast should be safe - Measurement::PhysicalQuantity const physicalQuantity = buddy.getUiAmountWithUnits().getPhysicalQuantity(); - this->pimpl->m_contextMenu = UnitAndScalePopUpMenu::create(this->pimpl->m_parent, - physicalQuantity, - forcedSystemOfMeasurement, - forcedRelativeScale); - return; -} +///void SmartLabel::initializeSection() { +/// if (!this->pimpl->m_configSection.isEmpty()) { +/// // We're already initialised +/// return; +/// } +/// +/// // +/// // If the label has the pimpl->m_configSection defined, use it +/// // otherwise, if the paired field has a pimpl->m_configSection, use it +/// // otherwise, if the parent object has a pimpl->m_configSection, use it +/// // if all else fails, get the parent's object name +/// // +/// if (this->property(*PropertyNames::SmartField::configSection).isValid()) { +/// this->pimpl->m_configSection = this->property(*PropertyNames::SmartField::configSection).toString(); +/// return; +/// } +/// +/// // As much as I dislike it, dynamic properties can't be referenced on initialization. +/// QWidget const * mybuddy = this->buddy(); +/// if (mybuddy && mybuddy->property(*PropertyNames::SmartField::configSection).isValid() ) { +/// this->pimpl->m_configSection = mybuddy->property(*PropertyNames::SmartField::configSection).toString(); +/// return; +/// } +/// +/// if (this->pimpl->m_parent->property(*PropertyNames::SmartField::configSection).isValid() ) { +/// this->pimpl->m_configSection = +/// this->pimpl->m_parent->property(*PropertyNames::SmartField::configSection).toString(); +/// return; +/// } +/// +/// qWarning() << Q_FUNC_INFO << "this failed" << this; +/// this->pimpl->m_configSection = this->pimpl->m_parent->objectName(); +/// return; +///} +/// +///void SmartLabel::initializeProperty() { +/// +/// if (!this->pimpl->m_propertyName.isEmpty()) { +/// return; +/// } +/// +/// QWidget* mybuddy = this->buddy(); +/// if (this->property("editField").isValid()) { +/// this->pimpl->m_propertyName = this->property("editField").toString(); +/// } else if (mybuddy && mybuddy->property("editField").isValid()) { +/// this->pimpl->m_propertyName = mybuddy->property("editField").toString(); +/// } else { +/// qWarning() << Q_FUNC_INFO << "That failed miserably"; +/// } +/// return; +///} void SmartLabel::popContextMenu(const QPoint& point) { + Q_ASSERT(this->pimpl->m_initialised); + // For the moment, at least, we do not allow people to choose date formats per-field. (Although you might want to // mix and match metric and imperial systems in certain circumstances, it's less clear that there's a benefit to // mixing and matching date formats.) - auto const fieldType = this->getBuddy().getFieldType(); - if (!std::holds_alternative(fieldType)) { + if (!std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)) { return; } @@ -212,9 +305,7 @@ void SmartLabel::popContextMenu(const QPoint& point) { return; } - this->initializeProperty(); - this->initializeSection(); - this->initializeMenu(); + this->pimpl->initializeMenu(); // Show the pop-up menu and get back whatever the user seleted QAction * invoked = this->pimpl->m_contextMenu->exec(widgie->mapToGlobal(point)); @@ -223,30 +314,8 @@ void SmartLabel::popContextMenu(const QPoint& point) { } // Save the current settings (which may come from system-wide defaults) for the signal below - Q_ASSERT(std::holds_alternative(fieldType)); - Measurement::PhysicalQuantity physicalQuantity = std::get(fieldType); - PreviousScaleInfo previousScaleInfo{ - Measurement::getSystemOfMeasurementForField(this->pimpl->m_propertyName, this->pimpl->m_configSection, physicalQuantity), - Measurement::getForcedRelativeScaleForField(this->pimpl->m_propertyName, this->pimpl->m_configSection) - }; - - qDebug() << - Q_FUNC_INFO << "Property Name:" << this->pimpl->m_propertyName << ", Config Section" << - this->pimpl->m_configSection; - - // To make this all work, we need to set ogMin and ogMax when og is set etc - QVector fieldsToSet; - fieldsToSet.append(this->pimpl->m_propertyName); - if (this->pimpl->m_propertyName == "og") { - fieldsToSet.append(QString(*PropertyNames::Style::ogMin)); - fieldsToSet.append(QString(*PropertyNames::Style::ogMax)); - } else if (this->pimpl->m_propertyName == "fg") { - fieldsToSet.append(QString(*PropertyNames::Style::fgMin)); - fieldsToSet.append(QString(*PropertyNames::Style::fgMax)); - } else if (this->pimpl->m_propertyName == "color_srm") { - fieldsToSet.append(QString(*PropertyNames::Style::colorMin_srm)); - fieldsToSet.append(QString(*PropertyNames::Style::colorMax_srm)); - } + Q_ASSERT(std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)); + SmartAmounts::ScaleInfo const previousScaleInfo = this->getScaleInfo(); // User will either have selected a SystemOfMeasurement or a UnitSystem::RelativeScale. We can know which based on // whether it's the menu or the sub-menu that it came from. @@ -256,54 +325,26 @@ void SmartLabel::popContextMenu(const QPoint& point) { std::optional whatSelected = UnitAndScalePopUpMenu::dataFromQAction(*invoked); qDebug() << Q_FUNC_INFO << "Selected SystemOfMeasurement" << whatSelected; - if (!whatSelected) { - // Null means "Default", which means don't set a forced SystemOfMeasurement for this field - for (auto field : fieldsToSet) { - Measurement::setForcedSystemOfMeasurementForField(field, this->pimpl->m_configSection, std::nullopt); - } - } else { - for (auto field : fieldsToSet) { - Measurement::setForcedSystemOfMeasurementForField(field, this->pimpl->m_configSection, *whatSelected); - } - } + // This stores the new SystemOfMeasurement (if any), but modification of the field contents + // won't happen until it receives the signal sent below. + this->setForcedSystemOfMeasurement(whatSelected); // Choosing a forced SystemOfMeasurement resets any selection of forced RelativeScale - for (auto field : fieldsToSet) { - Measurement::setForcedRelativeScaleForField(field, this->pimpl->m_configSection, std::nullopt); - } - - // - // Hmm. For the color fields, we want to include the ecb or srm in the label text here. - // - // Assert that we already bailed above for fields that aren't a PhysicalQuantity, so we know std::get won't throw - // here. - // - auto const fieldType = this->getBuddy().getFieldType(); - Q_ASSERT(std::holds_alternative(fieldType)); - if (Measurement::PhysicalQuantity::Color == std::get(fieldType)) { - Measurement::UnitSystem const & disp = - Measurement::getUnitSystemForField(this->pimpl->m_propertyName, - this->pimpl->m_configSection, - Measurement::PhysicalQuantity::Color); - this->setText(tr("Color (%1)").arg(disp.unit()->name)); - } + this->setForcedRelativeScale(std::nullopt); } else { // It's the sub-menu, so UnitSystem::RelativeScale std::optional whatSelected = UnitAndScalePopUpMenu::dataFromQAction(*invoked); qDebug() << Q_FUNC_INFO << "Selected RelativeScale" << whatSelected; - if (!whatSelected) { - // Null means "Default", which means don't set a forced RelativeScale for this field - for (auto field : fieldsToSet) { - Measurement::setForcedRelativeScaleForField(field, this->pimpl->m_configSection, std::nullopt); - } - } else { - for (auto field : fieldsToSet) { - Measurement::setForcedRelativeScaleForField(field, this->pimpl->m_configSection, *whatSelected); - } - } + // This stores the new SystemOfMeasurement (if any), but modification of the field contents + // won't happen until it receives the signal sent below. + this->setForcedRelativeScale(whatSelected); } // Remember, we need the original unit, not the new one. + // + // Note that the changedSystemOfMeasurementOrScale signal can also be received by other slots than the + // SmartField::lineChanged. This is why we use a signal and why we include the SmartAmounts::ScaleInfo data (which + // SmartField could work out itself). emit changedSystemOfMeasurementOrScale(previousScaleInfo); return; diff --git a/src/widgets/SmartLabel.h b/src/widgets/SmartLabel.h index 6105bc66..42f367df 100644 --- a/src/widgets/SmartLabel.h +++ b/src/widgets/SmartLabel.h @@ -31,9 +31,10 @@ #include "BtFieldType.h" #include "measurement/UnitSystem.h" -#include "UiAmountWithUnits.h" // For PreviousScaleInfo +#include "SmartField.h" // For SmartAmounts::ScaleInfo +#include "SmartAmounts.h" -class SmartLineEdit; +class SmartField; /*! * \class SmartLabel @@ -41,11 +42,11 @@ class SmartLineEdit; * \brief Performs the necessary magic to select display units for any label. Specifically, this allows the user to * right-click on the label for a field and select * (a) which unit system to use for that field (eg US Customary (mass), Imperial (mass) or Metric/SI (mass) - * for a weight field) + * for a weight field) - all done via \c Measurement::SystemOfMeasurement * (b) which units within that system to use for the field (eg kg, g, mg if the user has selected Metric/SI on - * a weight field). - * Moreover, the settings for each label are remembered (via PersistentSettings) for future times the program is - * run. + * a weight field) - all done via \c Measurement::UnitSystem::RelativeScale. + * Moreover, the settings for each label‡ are remembered (via \c PersistentSettings) for future times the program + * is run. * * This has been a rather hidden feature of the program as there were no visual clues that right-clicking on a * field label would bring up a useful menu (and it is not common behaviour in other software). Where possible, @@ -53,14 +54,54 @@ class SmartLineEdit; * • mouseover on the label underlines the label text (hopefully making the user think of a clickable link), * • where left-clicking would otherwise have no effect, it now has the same effect as right-click. * - * A \c SmartLabel will usually have a corresponding \c SmartLineEdit. These two widgets will be Qt buddies, - * which mostly just means that the \c SmartLineEdit accepts the input focus on behalf of the \c SmartLabel when - * the user types the label's shortcut key combination. (It also means we don't have to store a bunch of info in - * this object that we can just get from our buddy. Eg \c BtFieldType is stored in \c SmartLineEdit, so we don't - * also need to store it here in \c SmartLabel.) + * A \c SmartLabel will usually, but not always, have a corresponding \c SmartField. (HOWEVER, per the notes + * below, there are some cases where there is \b no corresponding \c SmartField and some cases where there is + * more than one corresponding \c SmartField.) These \c SmartLabel and the \c SmartField (or one of them + * if there is more than one) will be Qt buddies, which mostly just means that the \c SmartField accepts the + * input focus on behalf of the \c SmartLabel when the user types the label's shortcut key combination. (TBD: It + * also means we don't have to store a bunch of info in this object that we can just get from our buddy. Eg + * \c BtFieldType is stored in \c SmartField, so we don't also need to store it here in \c SmartLabel.) * - * When the \c SmartLabel needs to tell the \c SmartLineEdit that the \c UnitSystem etc has changed, it sends a - * \c changedUnitSystemOrScale signal. (Previously this signal was called \c labelChanged.) + * When the \c SmartLabel needs to tell the \c SmartField and/or other widgets (eg a range slider) that the + * \c UnitSystem etc has changed, it sends a \c changedUnitSystemOrScale signal. (Previously this signal was + * called \c labelChanged.) + * + * NOTE: There are some circumstances where a \c SmartLabel is associated with more than one \c SmartField. + * Typically this is where we have a min/max range (eg on a \c Style record in \c StyleEditor). The + * underlying \c QLabel can only have a single "buddy", which is the last one passed in via + * \c QLabel::setBuddy (typically done via the property system from code generated from a .ui file). + * HOWEVER by calling \c SmartLabel::addBuddy instead, then all buddies can be remembered by \c SmartLabel + * (even though the base \c QLabel will only retain the last set one). This means a change to forced + * \c SystemOfMeasurement or \c RelativeScale on the \c SmartLabel will be correctly remembered (via + * \c PersistentSettings) and applied to the multiple \c SmartField objects. + * IN PRACTICE, you don't need to worry about this, provided you call \c SMART_FIELD_INIT (or + * similar) for each \c SmartField, everything will be handled correctly. + * + * NOTE: There are also cases where a \c SmartLabel has no buddy. Eg, in \c MainWindow, we have a number of + * sliders that show predicted gravity for the \c Recipe along with the gravity range for the \c Style. + * + * NOTE: There is another set of edge cases where we have a \c SmartField that does not have a \c SmartLabel + * (just a \c QLabel). This is either because there is no scale or unit system to change (eg because the + * field is a \c NonPhysicalQuantity such as \c NonPhysicalQuantity::Percentage) or, because the field is + * being used in a conversion tool where we want the GUI to accept/show only one type of units, but we + * still want to leverage all the magical conversion that \c SmartField knows how to do. + * + * NOTE: Finally (I hope!) there is the case of \c BtTableModel subclasses. In such table models, some of the + * columns will have user-selectable \c SystemOfMeasurement and/or \c RelativeScale. In this case, the + * \c BtTableModel::ColumnInfo mini-class "owns" the settings. + * + * ‡ Previously, we stored the settings not "per \c SmartLabel" but "per \c SmartField". This is logical for + * the mainline case of (the \c SmartLabel has one \c SmartField). However, it makes things a bit + * complicated for the two edge cases: (a) the \c SmartLabel has more than one \c SmartField and (b) the + * \c SmartLabel has more than no \c SmartField + * + * + * This is extra work when + * a \c SmartLabel has two \c SmartField buddies, and creates the need for \c SmartLabel::addBuddy etc + * above. In some ways it would be simpler to use the name of the \c SmartLabel as the lookup key in + * \c PersistentSettings. For the moment, we do not as it would require extra complexity elsewhere. Eg, the + * \c SmartLabel does not currently know its name, so we'd need to inject it via an additional parameter on + * \c SmartField::init and some more work in the \c SMART_FIELD_INIT and related macros. */ class SmartLabel : public QLabel { Q_OBJECT @@ -78,14 +119,59 @@ class SmartLabel : public QLabel { virtual ~SmartLabel(); /** - * \brief Our "buddy" should always be a \c SmartLineEdit. This is a convenience function to get it without the - * caller having to downcast from \c QWidget etc. + * \brief Post-construction initialisation. Usually called from \c SmartFieldInit. + * NOTE that it is OK for this to be called multiple times when there are multiple \c SmartField "buddies" + * for this \c SmartLabel. * - * Note that the buddy relationship is not symmetric. Although it is easy to get the buddy of a \c QLabel (or - * derived class), it is not easy to go in the other direction. In other words, if you have the buddy of a - * \c QLabel, there is not built-in way in Qt to get back to the \c QLabel. + * \param editorName + * \param labelName + * \param labelFqName Fully qualified label name. Usually a combination of \c editorName and \c labelName. + * \param smartField Can be \c nullptr if there is no corresponding \c SmartField (eg because a slider is used + * instead). + */ + void init(char const * const editorName, + char const * const labelName, + char const * const labelFqName, + SmartField * smartField, + TypeInfo const & typeInfo); + + void setForcedSystemOfMeasurement(std::optional systemOfMeasurement); + void setForcedRelativeScale(std::optional relativeScale); + std::optional getForcedSystemOfMeasurement() const; + std::optional getForcedRelativeScale() const; + SmartAmounts::ScaleInfo getScaleInfo() const; + + /** + * \brief Returns the \c SystemOfMeasurement that should be used to display this field, based on the forced + * \c SystemOfMeasurement for the field if there is one or otherwise on the the system-wide default + * \c UnitSystem for the field's \c PhysicalQuantity. */ - SmartLineEdit & getBuddy() const; + Measurement::SystemOfMeasurement getDisplaySystemOfMeasurement() const; + + /** + * \brief Returns the \c UnitSystem that should be used to display this field, based on the forced + * \c SystemOfMeasurement for the field if there is one or otherwise on the the system-wide default + * \c UnitSystem for the field's \c PhysicalQuantity. + */ + Measurement::UnitSystem const & getDisplayUnitSystem() const; + + /** + * \brief Converts a measurement (aka amount) to its numerical equivalent in whatever units are configured for this + * field. + */ + double getAmountToDisplay(double const canonicalValue) const; + + /** + * \brief Converts a range (ie min/max pair) of measurements (aka amounts) to its numerical equivalent in whatever + * units are configured for this field. + * + * \param canonicalValueMin + * \param canonicalValueMax + * + * \return + */ + QPair getRangeToDisplay(double const canonicalValueMin, + double const canonicalValueMax) const; /** * \brief We override the \c QWidget event handlers \c enterEvent and \c leaveEvent to implement mouse-over effects @@ -134,15 +220,15 @@ public slots: * referring to the function signature in .ui files, we need to remember to escape '<' to "<" and '>' to * ">" because .ui files are XML. */ - void changedSystemOfMeasurementOrScale(PreviousScaleInfo previousScaleInfo); - -// Using protected instead of private allows me to not use the friends -// declaration -protected: - - void initializeSection(); - void initializeProperty(); - void initializeMenu(); + void changedSystemOfMeasurementOrScale(SmartAmounts::ScaleInfo previousScaleInfo); + +/// // Using protected instead of private allows me to not use the friends +/// // declaration +/// protected: +/// +/// void initializeSection(); +/// void initializeProperty(); +/// void initializeMenu(); private: // Private implementation details - see https://herbsutter.com/gotw/_100/ diff --git a/src/widgets/SmartLineEdit.cpp b/src/widgets/SmartLineEdit.cpp index 5a5b6eac..d5989ca2 100644 --- a/src/widgets/SmartLineEdit.cpp +++ b/src/widgets/SmartLineEdit.cpp @@ -29,8 +29,9 @@ #include #include "measurement/Measurement.h" -#include "UiAmountWithUnits.h" +#include "SmartField.h" #include "utils/OptionalHelpers.h" +#include "utils/TypeLookup.h" #include "widgets/SmartLabel.h" namespace { @@ -42,18 +43,26 @@ namespace { class SmartLineEdit::impl { public: impl(SmartLineEdit & self) : - m_self {self}, - m_initialised {false}, - m_name {"Uninitialised!"}, - m_typeInfo {nullptr}, - m_buddyLabel {nullptr}, - m_uiAmountWithUnits {nullptr}, - m_precision {3}, - m_maximalDisplayString{"100.000 srm"}, + m_self{self}, m_desiredWidthInPixels{0} { return; } - +/// impl(SmartLineEdit & self) : +/// m_self {self}, +/// m_initialised {false}, +/// m_editorName {"Uninitialised m_editorName!" }, +/// m_lineEditName {"Uninitialised m_lineEditName!" }, +/// m_lineEditlFqName {"Uninitialised m_lineEditlFqName!"}, +/// m_typeInfo {nullptr}, +/// m_fixedDisplayUnit {nullptr}, +/// m_smartBuddyLabel {nullptr}, +/// m_uiAmountWithUnits {nullptr}, +/// m_precision {3}, +/// m_maximalDisplayString{"100.000 srm"}, +/// m_desiredWidthInPixels{0} { +/// return; +/// } +/// ~impl() = default; void calculateDisplaySize(QString const & maximalDisplayString) { @@ -103,83 +112,6 @@ class SmartLineEdit::impl { return; } - /** - * \brief We want to have two different signatures of \c SmartLineEdit::init so we can catch missing parameters at - * compile time. Ultimately they both do pretty much the same work, by calling this function. - */ - void init(char const * const name, - TypeInfo const & typeInfo, - SmartLabel * buddyLabel, - std::optional const precision, - QString const & maximalDisplayString, - bool fixedUnitsAndScale = false) { - // It's a coding error to call this function twice on the same object, ie we should only initialise something once! - Q_ASSERT(!this->m_initialised); - - this->m_name = name; - this->m_typeInfo = &typeInfo; - this->m_buddyLabel = buddyLabel; - if (precision) { - // It's a coding error to specify precision for a field that's not a (possibly optional) double (or a float, - // but we don't use float) - Q_ASSERT(this->m_typeInfo->typeIndex == typeid(double) || - this->m_typeInfo->typeIndex == typeid(std::optional)); - - // It's a coding error if precision is not some plausible value. For the moment at least, we assert there are - // no envisageable circumstances where we need to show more than 3 decimal places - Q_ASSERT(*precision <= 3); - this->m_precision = *precision; - } - // For integers, there are no decimal places to show - if (this->m_typeInfo->typeIndex == typeid(int) || - this->m_typeInfo->typeIndex == typeid(unsigned int)) { - this->m_precision = 0; - } - this->m_maximalDisplayString = maximalDisplayString; - this->m_initialised = true; - - connect(&this->m_self, &QLineEdit::editingFinished, &this->m_self, &SmartLineEdit::onLineChanged); - if (!std::holds_alternative(*this->m_typeInfo->fieldType)) { - - // It's only meaningful to have a UiAmountWithUnits if we are dealing with a PhysicalQuantity. - // It's a coding error if we already created a UiAmountWithUnits - Q_ASSERT(!this->m_uiAmountWithUnits); - this->m_uiAmountWithUnits = - std::make_unique(this->m_self.parentWidget(), - ConvertToPhysicalQuantities(*typeInfo.fieldType)); - // In older versions of the code, we had the .ui file set the "editField" property on a subclass of BtLineEdit - // for the forcedSystemOfMeasurement and/or forcedRelativeScale to be stored in PersistentSettings. Now that - // we have a globally unique name set in init, we use that instead. - this->m_uiAmountWithUnits->setEditField (this->m_name ); - this->m_uiAmountWithUnits->setConfigSection("SmartLineEdit"); - - QString configSection = - this->m_self.property(*PropertyNames::UiAmountWithUnits::configSection).toString(); - qDebug() << Q_FUNC_INFO << "Config Section =" << configSection; - this->m_uiAmountWithUnits->setConfigSection(configSection); - - // Unless we specified that this is a "fixed" field (ie where the user cannot exercise the choices about units - // and scale that she otherwise would), it's a coding error if we didn't specify the buddy for something - // measuring a physical quantity - Q_ASSERT(fixedUnitsAndScale || this->m_buddyLabel); - - if (!fixedUnitsAndScale) { - // It's also a coding error if we are not the buddy of the label we think we are. However, we cannot test this - // here as the buddy's QLabel::setBuddy() hasn't necessarily yet been called from the code generated from the - // .ui file. What we can do, as belt-and-braces is call it here. - this->m_buddyLabel->setBuddy(&this->m_self); - - connect(this->m_buddyLabel, &SmartLabel::changedSystemOfMeasurementOrScale, &this->m_self, &SmartLineEdit::lineChanged); - } - } - - // We can work out (and store) our display size here, but we don't yet set it. The way the Designer UI Files work is - // to generate code that calls setters such as setMaximumWidth() etc, which would override anything we do too early - // on in the life of the object. To be safe therefore, we set our size when setText() is called. - this->calculateDisplaySize(maximalDisplayString); - return; - } - /** * \brief You can't pass in std::nullopt to setAmount as it's of type std::nullopt_t, so this saves us repeating some * code. @@ -192,231 +124,55 @@ class SmartLineEdit::impl { } SmartLineEdit & m_self; - bool m_initialised; - char const * m_name; - TypeInfo const * m_typeInfo; - SmartLabel * m_buddyLabel; - std::unique_ptr m_uiAmountWithUnits; - // "Precision" (ie number of decimal places to show) is used if and only the field is numeric. For int and unsigned - // int, it must always be 0. - unsigned int m_precision; - QString m_maximalDisplayString; int m_desiredWidthInPixels; }; -SmartLineEdit::SmartLineEdit(QWidget * parent) : QLineEdit(parent), pimpl{std::make_unique(*this)} { - return; -} +SmartLineEdit::SmartLineEdit(QWidget * parent) : + QLineEdit(parent), + SmartField{}, + pimpl{std::make_unique(*this)} { -SmartLineEdit::~SmartLineEdit() = default; + connect(this, &QLineEdit::editingFinished, this, &SmartLineEdit::onLineChanged); -void SmartLineEdit::init(char const * const name, - TypeInfo const & typeInfo, - SmartLabel & buddyLabel, - std::optional const precision, - QString const & maximalDisplayString) { - qDebug() << Q_FUNC_INFO << name << ":" << typeInfo; - - // It's a coding error to call this version of init with a NonPhysicalQuantity - Q_ASSERT(typeInfo.fieldType && !std::holds_alternative(*typeInfo.fieldType)); - - this->pimpl->init(name, typeInfo, &buddyLabel, precision, maximalDisplayString); return; } -void SmartLineEdit::init(char const * const name, - TypeInfo const & typeInfo, - std::optional const precision, - QString const & maximalDisplayString) { - qDebug() << Q_FUNC_INFO << name << ":" << typeInfo; - - // It's a coding error to call this version of init with anything other than a NonPhysicalQuantity. (If you hit this - // assert, the debug log above should tell you which field is emitting it!) - Q_ASSERT(typeInfo.fieldType && std::holds_alternative(*typeInfo.fieldType)); +SmartLineEdit::~SmartLineEdit() = default; - this->pimpl->init(name, typeInfo, nullptr, precision, maximalDisplayString); - return; +QString SmartLineEdit::getRawText() const { + return this->text(); } -void SmartLineEdit::initFixed(char const * const name, - TypeInfo const & typeInfo, - std::optional const precision, - QString const & maximalDisplayString) { - qDebug() << Q_FUNC_INFO << name << ":" << typeInfo; - - // Unlike the two init() functions, it's OK to call this function with either a PhysicalQuantity or a - // NonPhysicalQuantity. (Strictly speaking "fixed" is a given for a NonPhysicalQuantity, because there are no units - // or scales to change, but enforcing that as a requirement, would not, I think, help prevent any bugs.) - Q_ASSERT(typeInfo.fieldType); - - this->pimpl->init(name, typeInfo, nullptr, precision, maximalDisplayString, true); +void SmartLineEdit::setRawText(QString const & text) { + this->QLineEdit::setText(text); + this->pimpl->setDisplaySize(); return; } -BtFieldType const SmartLineEdit::getFieldType() const { - Q_ASSERT(this->pimpl->m_initialised); - return *this->pimpl->m_typeInfo->fieldType; -} - -TypeInfo const & SmartLineEdit::getTypeInfo() const { - Q_ASSERT(this->pimpl->m_initialised); - return *this->pimpl->m_typeInfo; -} - -UiAmountWithUnits & SmartLineEdit::getUiAmountWithUnits() const { - Q_ASSERT(this->pimpl->m_initialised); - Q_ASSERT(this->pimpl->m_uiAmountWithUnits); - return *this->pimpl->m_uiAmountWithUnits; -} - -Measurement::Amount SmartLineEdit::toCanonical() const { - Q_ASSERT(this->pimpl->m_initialised); - Q_ASSERT(this->pimpl->m_uiAmountWithUnits); - return this->pimpl->m_uiAmountWithUnits->rawToCanonical(this->text()); -} - -template -void SmartLineEdit::setAmount(std::optional amount) { - Q_ASSERT(this->pimpl->m_initialised); - - if (this->pimpl->m_typeInfo->typeIndex != typeid(T)) { - qCritical() << - Q_FUNC_INFO << this->pimpl->m_name << ": Trying to set wrong type; m_typeInfo=" << this->pimpl->m_typeInfo; - } - - if (!amount) { - this->pimpl->setNoAmount(); - return; - } - - this->setAmount(*amount); +void SmartLineEdit::connectSmartLabelSignal(SmartLabel & smartLabel) { + connect(&smartLabel, &SmartLabel::changedSystemOfMeasurementOrScale, this, &SmartLineEdit::lineChanged); return; } -// -// Instantiate the above template function 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.) -// -template void SmartLineEdit::setAmount(std::optional amount); -template void SmartLineEdit::setAmount(std::optional amount); -template void SmartLineEdit::setAmount(std::optional amount); - -template void SmartLineEdit::setAmount(T amount) { - Q_ASSERT(this->pimpl->m_initialised); - qDebug() << Q_FUNC_INFO << this->pimpl->m_name << "amount =" << amount; - - if (this->pimpl->m_typeInfo->typeIndex != typeid(T)) { - qCritical() << - Q_FUNC_INFO << this->pimpl->m_name << ": Trying to set wrong type; m_typeInfo=" << this->pimpl->m_typeInfo; - } - - if (std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)) { - // The field is not measuring a physical quantity so there are no units or unit conversions to handle - - NonPhysicalQuantity const nonPhysicalQuantity = - std::get(*this->pimpl->m_typeInfo->fieldType); - // It's a coding error if we're trying to pass a number in to a string field - Q_ASSERT(nonPhysicalQuantity != NonPhysicalQuantity::String); - - // For percentages, we'd like to show the % symbol after the number - QString symbol{""}; - if (NonPhysicalQuantity::Percentage == nonPhysicalQuantity) { - symbol = " %"; - } - - this->QLineEdit::setText( - Measurement::displayQuantity(amount, this->pimpl->m_precision) + symbol - ); - } else { - // The field is measuring a physical quantity - qDebug() << - Q_FUNC_INFO << this->pimpl->m_name << "forcedSystemOfMeasurement:" << - this->pimpl->m_uiAmountWithUnits->getForcedSystemOfMeasurement() << ", forcedRelativeScale:" << - this->pimpl->m_uiAmountWithUnits->getForcedRelativeScale(); - this->QLineEdit::setText( - this->pimpl->m_uiAmountWithUnits->displayAmount(amount, this->pimpl->m_precision) - ); - } - - this->pimpl->setDisplaySize(); +void SmartLineEdit::doPostInitWork() { + // We can work out (and store) our display size here, but we don't yet set it. The way the Designer UI Files work is + // to generate code that calls setters such as setMaximumWidth() etc, which would override anything we do too early + // on in the life of the object. To be safe therefore, we set our size when setText() is called. + this->pimpl->calculateDisplaySize(this->getMaximalDisplayString()); return; } -template void SmartLineEdit::setAmount(int amount); -template void SmartLineEdit::setAmount(unsigned int amount); -template void SmartLineEdit::setAmount(double amount); - -template T SmartLineEdit::getValueAs() const { - qDebug() << - Q_FUNC_INFO << this->pimpl->m_name << ": Converting" << this->text() << "to" << - Measurement::extractRawFromString(this->text()); - return Measurement::extractRawFromString(this->text()); -} -// -// Instantiate the above template function 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.) -// -template int SmartLineEdit::getValueAs() const; -template unsigned int SmartLineEdit::getValueAs() const; -template double SmartLineEdit::getValueAs() const; - - -//============================================ Property Getters and Setters ============================================ -// Note that we cannot assume init() has yet been run when these are called from (code generated from) a .ui file -/// -///void SmartLineEdit::setEditField (QString val) { this->pimpl->m_editField = val; if (this->pimpl->m_initialised) { this->pimpl->m_uiAmountWithUnits->setEditField (val); } return; } -///void SmartLineEdit::setConfigSection (QString val) { this->pimpl->m_configSection = val; if (this->pimpl->m_initialised) { this->pimpl->m_uiAmountWithUnits->setConfigSection (val); } return; } -/// // TODO Check where these two are used and whether we can eliminate them -///void SmartLineEdit::setForcedSystemOfMeasurementViaString(QString val) { Q_ASSERT(this->pimpl->m_initialised); this->pimpl->m_uiAmountWithUnits->setForcedSystemOfMeasurementViaString(val); return; } -///void SmartLineEdit::setForcedRelativeScaleViaString (QString val) { Q_ASSERT(this->pimpl->m_initialised); this->pimpl->m_uiAmountWithUnits->setForcedRelativeScaleViaString (val); return; } -/// -///QString SmartLineEdit::getEditField() const { if (this->pimpl->m_initialised) { return this->pimpl->m_uiAmountWithUnits->getEditField() ; } return this->pimpl->m_editField ; } -///QString SmartLineEdit::getConfigSection() /* not const */ { if (this->pimpl->m_initialised) { return this->pimpl->m_uiAmountWithUnits->getConfigSection() /* not const */ ; } return this->pimpl->m_configSection; } -///QString SmartLineEdit::getForcedSystemOfMeasurementViaString() const { Q_ASSERT(this->pimpl->m_initialised); return this->pimpl->m_uiAmountWithUnits->getForcedSystemOfMeasurementViaString(); } -///QString SmartLineEdit::getForcedRelativeScaleViaString() const { Q_ASSERT(this->pimpl->m_initialised); return this->pimpl->m_uiAmountWithUnits->getForcedRelativeScaleViaString() ; } - -//====================================================================================================================== - void SmartLineEdit::onLineChanged() { - Q_ASSERT(this->pimpl->m_initialised); + Q_ASSERT(this->isInitialised()); // .:TODO:. Finish work on optional - if ("" == this->text() && this->pimpl->m_typeInfo->isOptional()) { + if ("" == this->getRawText() && this->getTypeInfo().isOptional()) { this->pimpl->setNoAmount(); - } else if (std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)) { + } else if (std::holds_alternative(*this->getTypeInfo().fieldType)) { // The field is not measuring a physical quantity so there are no units or unit conversions to handle // However, for anything other than a string, we still want to parse out the numeric part of the input qDebug() << Q_FUNC_INFO; - - // .:TBD:. At the moment, the special handling here for types other than double is a bit moot, but we keep it in - // case we need to do more in future. - NonPhysicalQuantity const nonPhysicalQuantity = - std::get(*this->pimpl->m_typeInfo->fieldType); - if (nonPhysicalQuantity != NonPhysicalQuantity::String) { - bool ok = false; - if (this->pimpl->m_typeInfo->typeIndex == typeid(double)) { - double amount = Measurement::extractRawFromString(this->text(), &ok); - this->setAmount(amount); - } else if (this->pimpl->m_typeInfo->typeIndex == typeid(int)) { - int amount = Measurement::extractRawFromString(this->text(), &ok); - this->setAmount(amount); - } else if (this->pimpl->m_typeInfo->typeIndex == typeid(unsigned int)) { - unsigned int amount = Measurement::extractRawFromString(this->text(), &ok); - this->setAmount(amount); - } else { - // It's a coding error if we get here - qCritical() << Q_FUNC_INFO << this->pimpl->m_name << ": Don't know how to parse" << this->pimpl->m_typeInfo; - Q_ASSERT(false); - } - if (!ok) { - qWarning() << - Q_FUNC_INFO << this->pimpl->m_name << ": Unable to extract number from" << this->text() << "for" << - this->pimpl->m_typeInfo; - this->setAmount(0); - } - } + this->correctEnteredText(); if (sender() == this) { emit textModified(); @@ -425,48 +181,27 @@ void SmartLineEdit::onLineChanged() { } // The field is measuring a physical quantity - Q_ASSERT(this->pimpl->m_uiAmountWithUnits); - qDebug() << - Q_FUNC_INFO << this->pimpl->m_name << ": Field Type:" << *this->pimpl->m_typeInfo->fieldType << - ", forcedSystemOfMeasurement=" << this->pimpl->m_uiAmountWithUnits->getForcedSystemOfMeasurement() << - ", forcedRelativeScale=" << this->pimpl->m_uiAmountWithUnits->getForcedRelativeScale() << ", value=" << - this->text(); - - Measurement::PhysicalQuantities const physicalQuantities = ConvertToPhysicalQuantities(*this->pimpl->m_typeInfo->fieldType); - - QString const propertyName = this->pimpl->m_uiAmountWithUnits->getEditField(); - QString const configSection = this->pimpl->m_uiAmountWithUnits->getConfigSection(); - Measurement::SystemOfMeasurement const oldSystemOfMeasurement = - Measurement::getSystemOfMeasurementForField(propertyName, configSection, physicalQuantities); - auto oldForcedRelativeScale = Measurement::getForcedRelativeScaleForField(propertyName, configSection); - PreviousScaleInfo previousScaleInfo{ - oldSystemOfMeasurement, - oldForcedRelativeScale - }; + Q_ASSERT(!std::holds_alternative(*this->getTypeInfo().fieldType)); - qDebug() << - Q_FUNC_INFO << "propertyName=" << propertyName << ", configSection=" << configSection << - ", oldSystemOfMeasurement=" << oldSystemOfMeasurement << ", oldForcedRelativeScale=" << oldForcedRelativeScale; + auto const previousScaleInfo = this->getScaleInfo(); this->lineChanged(previousScaleInfo); return; } -void SmartLineEdit::lineChanged(PreviousScaleInfo previousScaleInfo) { - Q_ASSERT(this->pimpl->m_initialised); - Q_ASSERT(this->pimpl->m_uiAmountWithUnits); +void SmartLineEdit::lineChanged(SmartAmounts::ScaleInfo previousScaleInfo) { + Q_ASSERT(this->isInitialised()); + Q_ASSERT(!std::holds_alternative(*this->getTypeInfo().fieldType)); // editingFinished happens on focus being lost, regardless of anything // being changed. I am hoping this short circuits properly and we do // nothing if nothing changed. if (this->sender() == this && !this->isModified()) { - qDebug() << Q_FUNC_INFO << this->pimpl->m_name << ": Nothing changed; field holds" << this->text(); + qDebug() << Q_FUNC_INFO << this->getFqFieldName() << ": Nothing changed; field holds" << this->text(); return; } - this->QLineEdit::setText( - this->pimpl->m_uiAmountWithUnits->correctEnteredText(this->text(), this->pimpl->m_precision, previousScaleInfo) - ); + this->correctEnteredText(previousScaleInfo); if (sender() == this) { emit textModified(); diff --git a/src/widgets/SmartLineEdit.h b/src/widgets/SmartLineEdit.h index 86ed1af4..b93b5a2d 100644 --- a/src/widgets/SmartLineEdit.h +++ b/src/widgets/SmartLineEdit.h @@ -31,8 +31,10 @@ #include "BtFieldType.h" #include "utils/TypeLookup.h" -#include "UiAmountWithUnits.h" +#include "SmartField.h" +#include "SmartAmounts.h" +class QLabel; class SmartLabel; /*! @@ -64,137 +66,18 @@ class SmartLabel; * of lots of new field types for BeerJSON. Also, it had the disadvantage that you had to think about field * types when editing the .ui file rather than being able to leave such details to the corresponding .cpp file. */ -class SmartLineEdit : public QLineEdit { +class SmartLineEdit : public QLineEdit, public SmartField { Q_OBJECT - // These properties are needed so that the .ui files can uniquely identify each field and so that, for fields - // relating to physical quantities, the user can individually set the system of measurement and relative scale. - // - // They all pass through to UiAmountWithUnits -/// Q_PROPERTY(QString configSection READ getConfigSection WRITE setConfigSection STORED false) -/// Q_PROPERTY(QString editField READ getEditField WRITE setEditField STORED false) -/// Q_PROPERTY(QString forcedSystemOfMeasurement READ getForcedSystemOfMeasurementViaString WRITE setForcedSystemOfMeasurementViaString STORED false) -/// Q_PROPERTY(QString forcedRelativeScale READ getForcedRelativeScaleViaString WRITE setForcedRelativeScaleViaString STORED false) - public: SmartLineEdit(QWidget* parent = nullptr); virtual ~SmartLineEdit(); - /** - * \brief This needs to be called before the object is used, typically in constructor of whatever editor is using the - * widget. As well as passing in a bunch of info that cannot easily be given to the constructor (per comment - * above), it also ensures, if necessary, that the \c changedSystemOfMeasurementOrScale signal from the - * \c SmartLabel buddy is connected to the \c lineChanged slot of this \c SmartLineEdit. - * - * This version is for a \c PhysicalQuantity (or \c Mixed2PhysicalQuantities) field. - * - * Note, in reality, you actually use the \c SMART_LINE_EDIT_INIT macro (see below). - * - * \param name This should uniquely identify this field in the application. (Usually, it's a combination of the - * owning widget and the member variable, eg "FermentableEditor->lineEdit_color".) This serves two - * purposes: - * - For logging, it helps a lot with debugging. (We have hundreds of instances of this object - * and if we detect that one of them is misconfigured, it's very useful to be able to log which - * one!) - * - For fields where there is a choice of \c SystemOfMeasurement and/or \c RelativeScale, this - * provides a unique name against which to store the user's choice in \c PersistentSettings - * - * \param typeInfo Tells us what data type we use to store the contents of the field (when converted to - * canonical units if it is a \c PhysicalQuantity) and, whether this is an optional - * field (in which case we need to handle blank / empty string as a valid value). - * - * \param buddyLabel Required if \c fieldType is \b not a \c NonPhysicalQuantity - * - * \param precision For a decimal field, this determines the number of decimal places to show. If not - * specified, we show 3 decimal places. TBD: IDK if one day we might need to be more - * sophisticated about this, ie with number of decimal places dependent on the units that - * the user has chosen, but for now we assume it's the same for everything. - * \param maximalDisplayString Used for determining the width of the widget - */ - void init(char const * const name, - TypeInfo const & typeInfo, - SmartLabel & buddyLabel, - std::optional const precision = std::nullopt, - QString const & maximalDisplayString = "100.000 srm"); - - /** - * \brief As above, but for non-physical quantity such as \c NonPhysicalQuantity::Date, - * \c NonPhysicalQuantity::String, etc. - * - * The reason for having two versions of init() is to make it harder to forget the \c buddyLabel parameter - * when the field relates to a \c PhysicalQuantity. - * - * Note, in reality, you actually use the \c SMART_LINE_EDIT_INIT macro (see below). - */ - void init(char const * const name, - TypeInfo const & typeInfo, - std::optional const precision = std::nullopt, - QString const & maximalDisplayString = "100.000 srm"); - - /** - * \brief As \c init, but for a \c PhysicalQuantity (or \c Mixed2PhysicalQuantities) field where the user does \b not - * have a choice about units or scales (even though they otherwise would for this sort of - * \c PhysicalQuantity). This is typically used on conversion dialogs, eg \c RefractoDialog, where we are - * asking the user to give us inputs in specific units in order to convert them to other units measuring the - * same physical quantity. - * - * Note that we allow this to be used for a \c NonPhysicalQuantity too because, in this "fixed" case, there is - * no concern about needing to remember whether or not to specify the buddy label. - */ - void initFixed(char const * const name, - TypeInfo const & typeInfo, - std::optional const precision = std::nullopt, - QString const & maximalDisplayString = "100.000 srm"); - - BtFieldType const getFieldType() const; - - TypeInfo const & getTypeInfo() const; - - /** - * \brief If our field type is \b not \c NonPhysicalQuantity, then this returns the \c UiAmountWithUnits for handling - * units. (It is a coding error to call this function if our field type \c is \c NonPhysicalQuantity.) - */ - UiAmountWithUnits & getUiAmountWithUnits() const; - - /** - * \brief If our field type is \b not \c NonPhysicalQuantity, then this returns the field converted to canonical - * units for the relevant \c Measurement::PhysicalQuantity. (It is a coding error to call this function if - * our field type \c is \c NonPhysicalQuantity.) - */ - Measurement::Amount toCanonical() const; - - /** - * \brief Set the amount for a numeric field - * - * \param amount is the amount to display, but the field should be blank if this is \b std::nullopt - */ - template void setAmount(std::optional amount); - template void setAmount(T amount); - -// /** -// * \brief Set the text for a non-numeric field -// */ -// void setText(QString amount, std::optional precision = std::nullopt); - - /** - * \brief Use this when you want to get the text as a number (and ignore any units or other trailling letters or - * symbols) - */ - template T getValueAs() const; - - //========================================== Property Getters and Setters =========================================== -/// void setEditField(QString editField); -/// void setConfigSection(QString configSection); -/// void setForcedSystemOfMeasurementViaString(QString systemOfMeasurementAsString); -/// void setForcedRelativeScaleViaString(QString relativeScaleAsString); -/// -/// QString getEditField() const; -/// QString getConfigSection(); // This does lazy-loading so isn't const -/// QString getForcedSystemOfMeasurementViaString() const; -/// QString getForcedRelativeScaleViaString() const; - //=================================================================================================================== - + virtual QString getRawText() const; + virtual void setRawText(QString const & text); + virtual void connectSmartLabelSignal(SmartLabel & smartLabel); + virtual void doPostInitWork(); public slots: /** @@ -210,7 +93,7 @@ public slots: * In previous versions of the code, this was mostly referenced in .ui files. One disadvantage of that * approach was that the signal connections were only checked at run-time. */ - void lineChanged(PreviousScaleInfo previousScaleInfo); + void lineChanged(SmartAmounts::ScaleInfo previousScaleInfo); signals: /** @@ -228,76 +111,4 @@ public slots: std::unique_ptr pimpl; }; -/** - * \brief Helper macro for \c SMART_LINE_EDIT_INIT. Essentially does concatenation, using the magic, that, for the - * compiler, there is no difference between writing a string literal as: - * "foobarhumbug" - * and writing it as: - * "foo" "bar" "humbug" - */ -#define SLE_LOG_NAME(editorClass, fieldName) #editorClass "->" #fieldName - -/** - * \brief This macro saves a bit of copy-and-paste when invoking \c SmartLineEdit::init. Eg instead of writing: - * - * this->lineEdit_color->init("FermentableEditor->lineEdit_color", Fermentable::typeLookup.getType(PropertyNames::Fermentable::color_srm), *this->label_color, 0); - * - * you write: - * - * SMART_LINE_EDIT_INIT(FermentableEditor, Fermentable, lineEdit_color, PropertyNames::Fermentable::color_srm, *this->label_color, 0); - * - * \param editorClass The class name of the class holding the field we're initialising, eg \c HopEditor. (In theory we - * could pick this up via \c staticMetaObject.className(), but then we wouldn't be able to do the - * macro concatenation here.) - * \param modelClass The subclass of \c NamedEntity that we're editing. Eg in \c HopEditor, this will be \c Hop - * \param fieldName The name of the member variable for this field, eg in \c HopEditor, this could be \c lineEdit_name, - * \c lineEdit_alpha, etc. Note that: - * - We deliberately don't try to do anything clever with automatically inserting the "lineEdit_" - * prefix, as this would make the code harder to read and search. - * - It is intentional that field names sometimes differ slightly from property names. The latter - * always include their canonical unit names (eg \c PropertyNames::Fermentable::color_srm) whereas - * the former do not (eg \c lineEdit_color) because the user can enter data in any supported - * units. - * \param propertyName The name of the property to which this field relates, eg in \c HopEditor, this could be - * \c PropertyNames::NamedEntity::name, \c PropertyNames::Hop::alpha_pct, etc. (Note, as above, we - * intentionally do \b not automatically insert the \c PropertyNames:: prefix.) - * \param ... Any remaining arguments are passed through to \c SmartLineEdit::init in third position and above - * Note that the introduction of __VA_OPT__ in C++20 makes our lives easier here. - */ -#define SMART_LINE_EDIT_INIT(editorClass, modelClass, fieldName, propertyName, ...) \ - this-> fieldName ->init(SLE_LOG_NAME(editorClass, fieldName), modelClass ::typeLookup.getType(propertyName) __VA_OPT__(, __VA_ARGS__)) - -/** - *\brief An alternate version of \c SMART_LINE_EDIT_INIT for use when there is no \c modelClass (eg in a free-standing - * calculation dialog that does not update the model). Instead of writing: - * - * static auto const typeInfoFor_lineEdit_temp = TypeInfo::construct(Measurement::PhysicalQuantity::Temperature); - * this->lineEdit_temp->init("PrimingDialog->lineEdit_temp", typeInfoFor_lineEdit_temp, *this->label_temp, 1); - * - * you write: - * - * SMART_LINE_EDIT_INIT_FS(PrimingDialog, lineEdit_temp, double, Measurement::PhysicalQuantity::Temperature, *this->label_temp, 1); - * - * The _FS in the name stands for "free-standing". - * - * \param editorClass As for \c SMART_LINE_EDIT_INIT. - * \param fieldName As for \c SMART_LINE_EDIT_INIT - * \param nativeType The native type in which this value is / would be stored, eg double - * \param btFieldType The \c BtFieldType for this field. Together with \c nativeType, this is used to construct a - * static local \c TypeInfo struct to pass by reference to \c SmartLineEdit::init. - * \param ... Any remaining arguments are passed through to \c SmartLineEdit::init in third position and above - */ -#define SMART_LINE_EDIT_INIT_FS(editorClass, fieldName, nativeType, btFieldType, ...) \ - static auto const typeInfoFor_##fieldName = TypeInfo::construct(btFieldType); \ - this-> fieldName ->init(SLE_LOG_NAME(editorClass, fieldName), typeInfoFor_##fieldName __VA_OPT__(, __VA_ARGS__)) - -/** - * \brief A alternate version of \c SMART_LINE_EDIT_INIT_FS that calls \c SmartLineEdit::initFixed instead of - * \c SmartLineEdit::init. This is what you use for fields where we want to remove the choice of units and scale - * from the user, as explained in comments for \c SmartLineEdit::initFixed. - */ -#define SMART_LINE_EDIT_INIT_FS_FIXED(editorClass, fieldName, nativeType, btFieldType, ...) \ - static auto const typeInfoFor_##fieldName = TypeInfo::construct(btFieldType); \ - this-> fieldName ->initFixed(SLE_LOG_NAME(editorClass, fieldName), typeInfoFor_##fieldName __VA_OPT__(, __VA_ARGS__)) - #endif diff --git a/translations/bt_ca.ts b/translations/bt_ca.ts index 6c07cfd3..7f5c9053 100644 --- a/translations/bt_ca.ts +++ b/translations/bt_ca.ts @@ -511,22 +511,22 @@ BtDigitWidget Too low for style. - Massa baix per l'estil. + Massa baix per l'estil. In range for style. - Correcte per l'estil. + Correcte per l'estil. Too high for style. - Massa alt per l'estil. + Massa alt per l'estil.
BtLabel Color (%1) - Color (%1) + Color (%1) @@ -4278,7 +4278,7 @@ El volum final al primari és de %1. Bad column: %1 - Columna errònia: %1 + Columna errònia: %1 @@ -4322,11 +4322,26 @@ El volum final al primari és de %1. L'equip i el macerat s'han reiniciat a causa de que les temperatures del macerat no s'escalen fàcilment. Cal reiniciar l'assistent del macerat. + + SmartDigitWidget + + Too low for style. + Massa baix per l'estil. + + + In range for style. + Correcte per l'estil. + + + Too high for style. + Massa alt per l'estil. + + SmartLabel Color (%1) - Color (%1) + Color (%1) @@ -8263,6 +8278,14 @@ El volum final al primari és de %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_cs.ts b/translations/bt_cs.ts index 59980245..feef921f 100644 --- a/translations/bt_cs.ts +++ b/translations/bt_cs.ts @@ -471,22 +471,15 @@ BtDigitWidget Too low for style. - Příliš nízké pro tento styl. + Příliš nízké pro tento styl. In range for style. - V rozsahu stylu. + V rozsahu stylu. Too high for style. - Příliš vysoké pro tento styl. - - - - BtLabel - - Color (%1) - + Příliš vysoké pro tento styl. @@ -4210,7 +4203,7 @@ Celkový objem pro hlavní kvašení je %1. Bad column: %1 - Chybný sloupec: %1 + Chybný sloupec: %1 @@ -4255,10 +4248,18 @@ Celkový objem pro hlavní kvašení je %1. - SmartLabel + SmartDigitWidget - Color (%1) - + Too low for style. + Příliš nízké pro tento styl. + + + In range for style. + V rozsahu stylu. + + + Too high for style. + Příliš vysoké pro tento styl. @@ -8181,6 +8182,14 @@ Celkový objem pro hlavní kvašení je %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_de.ts b/translations/bt_de.ts index 28667911..fc4e3d7b 100644 --- a/translations/bt_de.ts +++ b/translations/bt_de.ts @@ -503,22 +503,22 @@ BtDigitWidget Too low for style. - Zu niedrig für diesen Typ. + Zu niedrig für diesen Typ. In range for style. - Im Bereich dieses Typs. + Im Bereich dieses Typs. Too high for style. - Zu hoch für diesen Typ. + Zu hoch für diesen Typ. BtLabel Color (%1) - Farbe (%1) + Farbe (%1) @@ -4246,7 +4246,7 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. Bad column: %1 - Fehlerhafte Spalte: %1 + Fehlerhafte Spalte: %1 @@ -4290,11 +4290,26 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. Die Ausrüstung und Maische wurden zurückgesetzt weil die Maischtemperaturen sich nicht einfach anpassen lassen. Bitte führen sie den Maische-Assistenten erneut aus. + + SmartDigitWidget + + Too low for style. + Zu niedrig für diesen Typ. + + + In range for style. + Im Bereich dieses Typs. + + + Too high for style. + Zu hoch für diesen Typ. + + SmartLabel Color (%1) - Farbe (%1) + Farbe (%1) @@ -8187,6 +8202,14 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_el.ts b/translations/bt_el.ts index 1aed55b8..bfbd0bf1 100644 --- a/translations/bt_el.ts +++ b/translations/bt_el.ts @@ -471,22 +471,15 @@ BtDigitWidget Too low for style. - Κάτω από τα όρια του στυλ + Κάτω από τα όρια του στυλ In range for style. - Στα όρια του στυλ + Στα όρια του στυλ Too high for style. - Πάνω από τα όρια του στυλ - - - - BtLabel - - Color (%1) - + Πάνω από τα όρια του στυλ @@ -4214,7 +4207,7 @@ The final volume in the primary is %1. Bad column: %1 - Προβληματική στήλη: %1 + Προβληματική στήλη: %1 @@ -4259,10 +4252,18 @@ The final volume in the primary is %1. - SmartLabel + SmartDigitWidget - Color (%1) - + Too low for style. + Κάτω από τα όρια του στυλ + + + In range for style. + Στα όρια του στυλ + + + Too high for style. + Πάνω από τα όρια του στυλ @@ -8183,6 +8184,14 @@ The final volume in the primary is %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_en.ts b/translations/bt_en.ts index b040d70a..b861db84 100644 --- a/translations/bt_en.ts +++ b/translations/bt_en.ts @@ -277,28 +277,6 @@ - - BtDigitWidget - - Too low for style. - - - - In range for style. - - - - Too high for style. - - - - - BtLabel - - Color (%1) - - - BtPrintAndPreview @@ -3589,10 +3567,6 @@ The final volume in the primary is %1. % Acid - - Bad column: %1 - - ScaleRecipeEquipmentPage @@ -3636,9 +3610,17 @@ The final volume in the primary is %1. - SmartLabel + SmartDigitWidget + + Too low for style. + + - Color (%1) + In range for style. + + + + Too high for style. @@ -7009,6 +6991,14 @@ The final volume in the primary is %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_es.ts b/translations/bt_es.ts index 33b2ff4f..2c675943 100644 --- a/translations/bt_es.ts +++ b/translations/bt_es.ts @@ -511,22 +511,22 @@ BtDigitWidget Too low for style. - Demasiado bajo para el estilo. + Demasiado bajo para el estilo. In range for style. - Correcto para el estilo. + Correcto para el estilo. Too high for style. - Demasiado alto para el estilo. + Demasiado alto para el estilo. BtLabel Color (%1) - Color (%1) + Color (%1) @@ -4264,10 +4264,6 @@ El volumen final en el primario es %1. % Acid - - Bad column: %1 - - ScaleRecipeEquipmentPage @@ -4310,11 +4306,26 @@ El volumen final en el primario es %1. El equipo y la maceración se han reiniciado debido al hecho que las temperaturas de la maceración no se escalan facilmente. Por favor reiniciar el asistente de la maceración. + + SmartDigitWidget + + Too low for style. + Demasiado bajo para el estilo. + + + In range for style. + Correcto para el estilo. + + + Too high for style. + Demasiado alto para el estilo. + + SmartLabel Color (%1) - Color (%1) + Color (%1) @@ -8231,6 +8242,14 @@ El volumen final en el primario es %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_et.ts b/translations/bt_et.ts index f9594b17..997e0d2b 100644 --- a/translations/bt_et.ts +++ b/translations/bt_et.ts @@ -328,28 +328,6 @@ Samm - - BtDigitWidget - - Too low for style. - - - - In range for style. - - - - Too high for style. - - - - - BtLabel - - Color (%1) - - - BtPrintAndPreview @@ -3640,10 +3618,6 @@ The final volume in the primary is %1. % Acid - - Bad column: %1 - - ScaleRecipeEquipmentPage @@ -3687,9 +3661,17 @@ The final volume in the primary is %1. - SmartLabel + SmartDigitWidget + + Too low for style. + + - Color (%1) + In range for style. + + + + Too high for style. @@ -7075,6 +7057,14 @@ The final volume in the primary is %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_eu.ts b/translations/bt_eu.ts index 10f6ed9c..47019e79 100644 --- a/translations/bt_eu.ts +++ b/translations/bt_eu.ts @@ -336,28 +336,6 @@ %1. urratsa: %2 - - BtDigitWidget - - Too low for style. - - - - In range for style. - - - - Too high for style. - - - - - BtLabel - - Color (%1) - - - BtPrintAndPreview @@ -3652,10 +3630,6 @@ The final volume in the primary is %1. % Acid - - Bad column: %1 - - ScaleRecipeEquipmentPage @@ -3699,9 +3673,17 @@ The final volume in the primary is %1. - SmartLabel + SmartDigitWidget + + Too low for style. + + - Color (%1) + In range for style. + + + + Too high for style. @@ -7087,6 +7069,14 @@ The final volume in the primary is %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_fr.ts b/translations/bt_fr.ts index bf59208a..3c2288e8 100644 --- a/translations/bt_fr.ts +++ b/translations/bt_fr.ts @@ -511,22 +511,22 @@ BtDigitWidget Too low for style. - Trop bas pour le style. + Trop bas pour le style. In range for style. - Dans les limites du style. + Dans les limites du style. Too high for style. - Trop haut pour le style. + Trop haut pour le style. BtLabel Color (%1) - Couleur (%1) + Couleur (%1) @@ -4282,7 +4282,7 @@ Le volume final dans la cuve de fermentation est de %1. Bad column: %1 - Mauvaise colonne : %1 + Mauvaise colonne : %1 @@ -4326,11 +4326,26 @@ Le volume final dans la cuve de fermentation est de %1. L'équipement et l'empâtage ont été réinitialisés car la température d'empâtage est difficile à adapter. Faites à nouveau appel à l'assistant d'empâtage. + + SmartDigitWidget + + Too low for style. + Trop bas pour le style. + + + In range for style. + Dans les limites du style. + + + Too high for style. + Trop haut pour le style. + + SmartLabel Color (%1) - Couleur (%1) + Couleur (%1) @@ -8269,6 +8284,14 @@ Le volume final dans la cuve de fermentation est de %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_gl.ts b/translations/bt_gl.ts index 884ea60c..1daf809e 100644 --- a/translations/bt_gl.ts +++ b/translations/bt_gl.ts @@ -447,22 +447,15 @@ BtDigitWidget Too low for style. - Demasiado baixo para o estilo + Demasiado baixo para o estilo In range for style. - Axeitado para o estilo + Axeitado para o estilo Too high for style. - Demasiado alto para o estilo - - - - BtLabel - - Color (%1) - + Demasiado alto para o estilo @@ -3767,10 +3760,6 @@ The final volume in the primary is %1. % Acid - - Bad column: %1 - - ScaleRecipeEquipmentPage @@ -3814,10 +3803,18 @@ The final volume in the primary is %1. - SmartLabel + SmartDigitWidget - Color (%1) - + Too low for style. + Demasiado baixo para o estilo + + + In range for style. + Axeitado para o estilo + + + Too high for style. + Demasiado alto para o estilo @@ -7234,6 +7231,14 @@ The final volume in the primary is %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_hu.ts b/translations/bt_hu.ts index b5703f1a..81540d42 100644 --- a/translations/bt_hu.ts +++ b/translations/bt_hu.ts @@ -503,22 +503,22 @@ BtDigitWidget Too low for style. - Stílus szerint kevés + Stílus szerint kevés In range for style. - Stílusnak megfelelő + Stílusnak megfelelő Too high for style. - Stílus szerint sok + Stílus szerint sok BtLabel Color (%1) - Szín (%1) + Szín (%1) @@ -4250,7 +4250,7 @@ Végleges mennyiség az elsődleges erjesztőben: %1 Bad column: %1 - Bad column: %1 + Bad column: %1 @@ -4294,11 +4294,26 @@ Végleges mennyiség az elsődleges erjesztőben: %1 Nem arányítható felszerelés és hőmérsékleti adatok. Indítsd újra a cefrézés varázslót! + + SmartDigitWidget + + Too low for style. + Stílus szerint kevés + + + In range for style. + Stílusnak megfelelő + + + Too high for style. + Stílus szerint sok + + SmartLabel Color (%1) - Szín (%1) + Szín (%1) @@ -8091,6 +8106,14 @@ Végleges mennyiség az elsődleges erjesztőben: %1 Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_it.ts b/translations/bt_it.ts index f70aa33c..469bd70c 100644 --- a/translations/bt_it.ts +++ b/translations/bt_it.ts @@ -523,22 +523,22 @@ BtDigitWidget Too low for style. - Troopo basso per lo stile. + Troopo basso per lo stile. In range for style. - Adeguato per lo stile. + Adeguato per lo stile. Too high for style. - Troppo alto per lo stile. + Troppo alto per lo stile. BtLabel Color (%1) - Colore (%1) + Colore (%1) @@ -4282,7 +4282,7 @@ Il Volume finale del primo è %1. Bad column: %1 - Cattiva colonna: %1 + Cattiva colonna: %1 @@ -4326,11 +4326,26 @@ Il Volume finale del primo è %1. Le attrezzature e il mash sono state azzerate a causa del fatto che le temperature non si sono abbassate facilmente. Si prega di eseguire nuovamente la procedura guidata di mosto. + + SmartDigitWidget + + Too low for style. + Troopo basso per lo stile. + + + In range for style. + Adeguato per lo stile. + + + Too high for style. + Troppo alto per lo stile. + + SmartLabel Color (%1) - Colore (%1) + Colore (%1) @@ -8269,6 +8284,14 @@ Il Volume finale del primo è %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_lv.ts b/translations/bt_lv.ts index 5290126e..221dfe48 100644 --- a/translations/bt_lv.ts +++ b/translations/bt_lv.ts @@ -383,28 +383,6 @@ Nezināms - - BtDigitWidget - - Too low for style. - - - - In range for style. - - - - Too high for style. - - - - - BtLabel - - Color (%1) - - - BtPrintAndPreview @@ -3707,10 +3685,6 @@ The final volume in the primary is %1. % Acid - - Bad column: %1 - - ScaleRecipeEquipmentPage @@ -3754,9 +3728,17 @@ The final volume in the primary is %1. - SmartLabel + SmartDigitWidget + + Too low for style. + + - Color (%1) + In range for style. + + + + Too high for style. @@ -7186,6 +7168,14 @@ The final volume in the primary is %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_nb.ts b/translations/bt_nb.ts index 71cc765f..b77d5fa2 100644 --- a/translations/bt_nb.ts +++ b/translations/bt_nb.ts @@ -471,22 +471,15 @@ BtDigitWidget Too low for style. - For lav for øltypen + For lav for øltypen In range for style. - Innenfor område til øltypen + Innenfor område til øltypen Too high for style. - For høy for øltypen - - - - BtLabel - - Color (%1) - + For høy for øltypen @@ -4208,10 +4201,6 @@ Sluttvolumet i primærgjæringskaret er %1. % Acid - - Bad column: %1 - - ScaleRecipeEquipmentPage @@ -4255,10 +4244,18 @@ Sluttvolumet i primærgjæringskaret er %1. - SmartLabel + SmartDigitWidget - Color (%1) - + Too low for style. + For lav for øltypen + + + In range for style. + Innenfor område til øltypen + + + Too high for style. + For høy for øltypen @@ -8178,6 +8175,14 @@ Sluttvolumet i primærgjæringskaret er %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_nl.ts b/translations/bt_nl.ts index 27887c26..02568168 100644 --- a/translations/bt_nl.ts +++ b/translations/bt_nl.ts @@ -515,22 +515,22 @@ BtDigitWidget Too low for style. - Te laag voor deze stijl. + Te laag voor deze stijl. In range for style. - Conform stijl. + Conform stijl. Too high for style. - Te hoog voor deze stijl. + Te hoog voor deze stijl. BtLabel Color (%1) - Kleur (%1) + Kleur (%1) @@ -4282,10 +4282,6 @@ Het uiteindelijke volume in de hoofdvergisting is %1. % Acid - - Bad column: %1 - - ScaleRecipeEquipmentPage @@ -4328,11 +4324,26 @@ Het uiteindelijke volume in de hoofdvergisting is %1. Deapparatuur en maisch zijn gereset omdat de maisch temperatuur niet eenvoudig schaalt. Draai de maisch wizard opnieuw a.u.b. + + SmartDigitWidget + + Too low for style. + Te laag voor deze stijl. + + + In range for style. + Conform stijl. + + + Too high for style. + Te hoog voor deze stijl. + + SmartLabel Color (%1) - Kleur (%1) + Kleur (%1) @@ -8081,6 +8092,14 @@ Het uiteindelijke volume in de hoofdvergisting is %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_pl.ts b/translations/bt_pl.ts index 9d20237e..dfab08a4 100644 --- a/translations/bt_pl.ts +++ b/translations/bt_pl.ts @@ -471,22 +471,15 @@ BtDigitWidget Too low for style. - Za niska wartość dla stylu. + Za niska wartość dla stylu. In range for style. - Wartość mieści się w stylu. + Wartość mieści się w stylu. Too high for style. - Za wysoka wartość dla stylu. - - - - BtLabel - - Color (%1) - + Za wysoka wartość dla stylu. @@ -4208,10 +4201,6 @@ Końcowa pojemność w fermentorze wyniesie %1. % Acid - - Bad column: %1 - - ScaleRecipeEquipmentPage @@ -4255,10 +4244,18 @@ Końcowa pojemność w fermentorze wyniesie %1. - SmartLabel + SmartDigitWidget - Color (%1) - + Too low for style. + Za niska wartość dla stylu. + + + In range for style. + Wartość mieści się w stylu. + + + Too high for style. + Za wysoka wartość dla stylu. @@ -8179,6 +8176,14 @@ Końcowa pojemność w fermentorze wyniesie %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_pt.ts b/translations/bt_pt.ts index 01ae4ad0..0fed0766 100644 --- a/translations/bt_pt.ts +++ b/translations/bt_pt.ts @@ -503,22 +503,22 @@ BtDigitWidget Too low for style. - Muito baixo para o estilo. + Muito baixo para o estilo. In range for style. - Dentro da Variação do Estilo. + Dentro da Variação do Estilo. Too high for style. - Muito Alto para o Estilo. + Muito Alto para o Estilo. BtLabel Color (%1) - Cor (%1) + Cor (%1) @@ -4228,10 +4228,6 @@ O volume final do fermentador primário é %1. % Acid - - Bad column: %1 - - ScaleRecipeEquipmentPage @@ -4274,11 +4270,26 @@ O volume final do fermentador primário é %1. O equipamento e esquema de mosturação devem ser redefinidos devido à dificuldade de dimensionar as temperaturas de mosturação. Por favor, execute novamente o assistente de mosturação. + + SmartDigitWidget + + Too low for style. + Muito baixo para o estilo. + + + In range for style. + Dentro da Variação do Estilo. + + + Too high for style. + Muito Alto para o Estilo. + + SmartLabel Color (%1) - Cor (%1) + Cor (%1) @@ -8211,6 +8222,14 @@ O volume final do fermentador primário é %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_ru.ts b/translations/bt_ru.ts index 203bcb31..fd148090 100644 --- a/translations/bt_ru.ts +++ b/translations/bt_ru.ts @@ -507,22 +507,22 @@ BtDigitWidget Too low for style. - Слишком низко для стиля. + Слишком низко для стиля. In range for style. - В пределах стиля. + В пределах стиля. Too high for style. - Слишком высоко для стиля. + Слишком высоко для стиля. BtLabel Color (%1) - Цвет (%1) + Цвет (%1) @@ -4260,10 +4260,6 @@ The final volume in the primary is %1. % Acid - - Bad column: %1 - - ScaleRecipeEquipmentPage @@ -4306,11 +4302,26 @@ The final volume in the primary is %1. Оборудование и затор были сброшены т.к. температура затора не маштабируется. Пожалуйсто перезапустите визард затора. + + SmartDigitWidget + + Too low for style. + Слишком низко для стиля. + + + In range for style. + В пределах стиля. + + + Too high for style. + Слишком высоко для стиля. + + SmartLabel Color (%1) - Цвет (%1) + Цвет (%1) @@ -8219,6 +8230,14 @@ The final volume in the primary is %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_sr.ts b/translations/bt_sr.ts index a5a5aadc..27dcac13 100644 --- a/translations/bt_sr.ts +++ b/translations/bt_sr.ts @@ -471,22 +471,15 @@ BtDigitWidget Too low for style. - Первише ниско за изабрани стил. + Первише ниско за изабрани стил. In range for style. - Унутар граница за изабрани стил. + Унутар граница за изабрани стил. Too high for style. - Превише високо за изабрани стил. - - - - BtLabel - - Color (%1) - + Превише високо за изабрани стил. @@ -4083,7 +4076,7 @@ The final volume in the primary is %1. Bad column: %1 - Лоша колона: %1 + Лоша колона: %1 @@ -4128,10 +4121,18 @@ The final volume in the primary is %1. - SmartLabel + SmartDigitWidget - Color (%1) - + Too low for style. + Первише ниско за изабрани стил. + + + In range for style. + Унутар граница за изабрани стил. + + + Too high for style. + Превише високо за изабрани стил. @@ -7636,6 +7637,14 @@ The final volume in the primary is %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_sv.ts b/translations/bt_sv.ts index 6114abcc..7b318491 100644 --- a/translations/bt_sv.ts +++ b/translations/bt_sv.ts @@ -511,22 +511,22 @@ BtDigitWidget Too low for style. - För låg för vald stil. + För låg för vald stil. In range for style. - I intervall för stilen. + I intervall för stilen. Too high for style. - För högt för stilen. + För högt för stilen. BtLabel Color (%1) - Färg (%1) + Färg (%1) @@ -4274,7 +4274,7 @@ Primärens slutgiltiga volym är %1. Bad column: %1 - Felaktig kolumn:%1 + Felaktig kolumn:%1 @@ -4318,11 +4318,26 @@ Primärens slutgiltiga volym är %1. Utrustningen och mäsken har återställts på grund av att mäskens temperatur inte enkelt går att skala, vänligen kör Skalningsguiden igen. + + SmartDigitWidget + + Too low for style. + För låg för vald stil. + + + In range for style. + I intervall för stilen. + + + Too high for style. + För högt för stilen. + + SmartLabel Color (%1) - Färg (%1) + Färg (%1) @@ -8111,6 +8126,14 @@ Primärens slutgiltiga volym är %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_tr.ts b/translations/bt_tr.ts index 173e2a22..a2e0c759 100644 --- a/translations/bt_tr.ts +++ b/translations/bt_tr.ts @@ -332,28 +332,6 @@ Adım %1: %2 - - BtDigitWidget - - Too low for style. - - - - In range for style. - - - - Too high for style. - - - - - BtLabel - - Color (%1) - - - BtPrintAndPreview @@ -3648,10 +3626,6 @@ The final volume in the primary is %1. % Acid - - Bad column: %1 - - ScaleRecipeEquipmentPage @@ -3695,9 +3669,17 @@ The final volume in the primary is %1. - SmartLabel + SmartDigitWidget + + Too low for style. + + - Color (%1) + In range for style. + + + + Too high for style. @@ -7076,6 +7058,14 @@ The final volume in the primary is %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/translations/bt_zh.ts b/translations/bt_zh.ts index 14d4b0b3..2ec1d60a 100644 --- a/translations/bt_zh.ts +++ b/translations/bt_zh.ts @@ -467,22 +467,15 @@ BtDigitWidget Too low for style. - 过低的风格。 + 过低的风格。 In range for style. - 在范围的风格 + 在范围的风格 Too high for style. - 过高的风格。 - - - - BtLabel - - Color (%1) - + 过高的风格。 @@ -4176,10 +4169,6 @@ The final volume in the primary is %1. % Acid - - Bad column: %1 - - ScaleRecipeEquipmentPage @@ -4223,10 +4212,18 @@ The final volume in the primary is %1. - SmartLabel + SmartDigitWidget - Color (%1) - + Too low for style. + 过低的风格。 + + + In range for style. + 在范围的风格 + + + Too high for style. + 过高的风格。 @@ -8127,6 +8124,14 @@ The final volume in the primary is %1. Edit Water + + Alkalinity + + + + Alkalinity measured as + + yeastEditor diff --git a/ui/brewNoteWidget.ui b/ui/brewNoteWidget.ui index cce241c1..b9ee8962 100644 --- a/ui/brewNoteWidget.ui +++ b/ui/brewNoteWidget.ui @@ -623,7 +623,7 @@ - + @@ -649,7 +649,7 @@ - + @@ -666,7 +666,7 @@ - + @@ -683,14 +683,14 @@ - + - + ABV based on user-reported FG @@ -700,7 +700,7 @@ - + @@ -717,7 +717,7 @@ - + @@ -734,7 +734,7 @@ - + @@ -798,9 +798,9 @@ - BtGenericDigit + SmartDigitWidget QLabel -
widgets/BtDigitWidget.h
+
widgets/SmartDigitWidget.h
diff --git a/ui/fermentableEditor.ui b/ui/fermentableEditor.ui index 7b717264..798509ad 100644 --- a/ui/fermentableEditor.ui +++ b/ui/fermentableEditor.ui @@ -44,7 +44,7 @@
- + Required diff --git a/ui/mainWindow.ui b/ui/mainWindow.ui index f8539c58..fb81a35b 100644 --- a/ui/mainWindow.ui +++ b/ui/mainWindow.ui @@ -374,7 +374,7 @@ - + false @@ -408,7 +408,7 @@ - + 0 @@ -443,7 +443,7 @@ - + 0 @@ -474,7 +474,7 @@ - + Qt::CustomContextMenu @@ -487,7 +487,7 @@ - + 0 @@ -578,7 +578,7 @@ - + 0 @@ -607,7 +607,7 @@ - + 0 @@ -626,7 +626,7 @@ - + 0 @@ -733,7 +733,7 @@ - + 0 @@ -830,7 +830,7 @@ - + Qt::CustomContextMenu @@ -872,7 +872,7 @@ - + Qt::CustomContextMenu @@ -885,7 +885,7 @@ - + Qt::CustomContextMenu @@ -905,17 +905,17 @@ - + Batch Size - rangeWidget_batchsize + rangeWidget_batchSize - + 0 @@ -937,7 +937,7 @@ - + Boil Size @@ -2187,30 +2187,20 @@ - BtTimeEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtTimeLabel + SmartLabel QLabel -
BtLabel.h
+
widgets/SmartLabel.h
- (PreviousScaleInfo) + changedSystemOfMeasurementOrScale(PreviousScaleInfo)
- BtGenericEdit + SmartLineEdit QLineEdit -
BtLineEdit.h
-
- - BtPercentageEdit - QLineEdit -
BtLineEdit.h
+
widgets/SmartLineEdit.h
+ + lineChanged(PreviousScaleInfo) +
CustomComboBox @@ -2222,51 +2212,11 @@ QWidget
StyleRangeWidget.h
- - BtVolumeEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtVolumeLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
MiscTreeView QTreeView
BtTreeView.h
- - BtDensityLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
- - BtDensityEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtColorLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
IbuGuSlider QWidget diff --git a/ui/mashDesigner.ui b/ui/mashDesigner.ui index 867a08e0..13cdf533 100644 --- a/ui/mashDesigner.ui +++ b/ui/mashDesigner.ui @@ -90,7 +90,7 @@
- + Qt::CustomContextMenu @@ -103,7 +103,7 @@ - + 150 @@ -116,7 +116,7 @@ - + Qt::CustomContextMenu @@ -129,7 +129,7 @@ - + 150 @@ -492,35 +492,19 @@ - BtTemperatureEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtTimeEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtTimeLabel + SmartLabel QLabel -
BtLabel.h
+
widgets/SmartLabel.h
changedSystemOfMeasurementOrScale(PreviousScaleInfo)
- BtTemperatureLabel - QLabel -
BtLabel.h
+ SmartLineEdit + QLineEdit +
widgets/SmartLineEdit.h
- changedSystemOfMeasurementOrScale(PreviousScaleInfo) + lineChanged(PreviousScaleInfo)
diff --git a/ui/mashEditor.ui b/ui/mashEditor.ui index e6f0850e..85d22308 100644 --- a/ui/mashEditor.ui +++ b/ui/mashEditor.ui @@ -25,7 +25,7 @@ - + 0 diff --git a/ui/mashStepEditor.ui b/ui/mashStepEditor.ui index 797e5461..c380e4d7 100644 --- a/ui/mashStepEditor.ui +++ b/ui/mashStepEditor.ui @@ -122,7 +122,7 @@ - + 0 @@ -141,7 +141,7 @@ - + 100 @@ -173,7 +173,7 @@ - + 0 @@ -192,7 +192,7 @@ - + 100 @@ -224,7 +224,7 @@ - + 0 @@ -243,7 +243,7 @@ - + 100 @@ -276,7 +276,7 @@ - + 0 @@ -295,7 +295,7 @@ - + 100 @@ -327,7 +327,7 @@ - + 0 @@ -346,7 +346,7 @@ - + 100 @@ -378,7 +378,7 @@ - + 0 @@ -397,7 +397,7 @@ - + 100 @@ -429,7 +429,7 @@ - + 0 @@ -448,7 +448,7 @@ - + 100 @@ -493,49 +493,17 @@ - BtTimeEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtTimeLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
- - BtVolumeLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
- - BtVolumeEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtTemperatureLabel + SmartLabel QLabel -
BtLabel.h
+
widgets/SmartLabel.h
changedSystemOfMeasurementOrScale(PreviousScaleInfo)
- BtTemperatureEdit + SmartLineEdit QLineEdit -
BtAmountEdit.h
+
widgets/SmartLineEdit.h
lineChanged(PreviousScaleInfo) diff --git a/ui/namedMashEditor.ui b/ui/namedMashEditor.ui index 8853b8ad..2e671bec 100644 --- a/ui/namedMashEditor.ui +++ b/ui/namedMashEditor.ui @@ -97,7 +97,7 @@
- + 0 @@ -120,7 +120,7 @@ - + 0 @@ -139,7 +139,7 @@ - + 0 @@ -155,9 +155,6 @@ Initial grain temp - - grainTemp_c - @@ -165,7 +162,7 @@ - + 0 @@ -184,7 +181,7 @@ - + 0 @@ -200,9 +197,6 @@ Sparge temp target - - spargeTemp_c - @@ -210,7 +204,7 @@ - + 0 @@ -226,7 +220,7 @@ - + 0 @@ -242,9 +236,6 @@ Sparge pH - - ph - @@ -295,7 +286,7 @@ - + 0 @@ -314,7 +305,7 @@ - + 0 @@ -330,9 +321,6 @@ Initial tun temp - - tunTemp_c - @@ -383,7 +371,7 @@ - + 0 @@ -402,7 +390,7 @@ - + 0 @@ -418,9 +406,6 @@ Tun mass - - tunWeight_kg - @@ -428,7 +413,7 @@ - + 0 @@ -444,7 +429,7 @@ - + 0 @@ -460,9 +445,6 @@ Tun specific heat (cal/(g*K)) - - tunSpecificHeat_calGC - @@ -608,55 +590,24 @@ - BtSpecificHeatCapacityEdit - QLineEdit -
BtAmountEdit.h
-
- - BtAcidityEdit - QLineEdit -
BtAmountEdit.h
-
- - BtTemperatureEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtTemperatureLabel + SmartLabel QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
- - BtMassLabel - QLabel -
BtLabel.h
+
widgets/SmartLabel.h
changedSystemOfMeasurementOrScale(PreviousScaleInfo) popContextMenu(QPoint)
- BtMassEdit + SmartLineEdit QLineEdit -
BtAmountEdit.h
+
widgets/SmartLineEdit.h
textModified() lineChanged() lineChanged(PreviousScaleInfo)
- - BtGenericEdit - QLineEdit -
BtLineEdit.h
-
CustomComboBox QComboBox diff --git a/ui/ogAdjuster.ui b/ui/ogAdjuster.ui index a7b6feaa..163cbf7a 100644 --- a/ui/ogAdjuster.ui +++ b/ui/ogAdjuster.ui @@ -46,7 +46,7 @@
- + 0 @@ -75,7 +75,7 @@ - + 0 @@ -94,7 +94,7 @@ - + 0 @@ -126,7 +126,7 @@ - + Qt::CustomContextMenu @@ -139,7 +139,7 @@ - + 0 @@ -232,7 +232,7 @@ - + 0 @@ -297,7 +297,7 @@ - + Qt::CustomContextMenu @@ -310,7 +310,7 @@ - + 0 @@ -369,7 +369,7 @@ - + 0 @@ -401,7 +401,7 @@ - + 0 @@ -420,7 +420,7 @@ - + 0 @@ -455,7 +455,7 @@ - + 0 @@ -474,7 +474,7 @@ - + 0 @@ -500,7 +500,7 @@ true - batchsize + batchSize @@ -574,41 +574,17 @@ - BtDensityEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtTemperatureEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtTemperatureLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
- - BtVolumeLabel + SmartLabel QLabel -
BtLabel.h
+
widgets/SmartLabel.h
changedSystemOfMeasurementOrScale(PreviousScaleInfo)
- BtVolumeEdit + SmartLineEdit QLineEdit -
BtAmountEdit.h
+
widgets/SmartLineEdit.h
lineChanged(PreviousScaleInfo) @@ -649,7 +625,7 @@ - label_batchsize + label_batchSize changedSystemOfMeasurementOrScale(PreviousScaleInfo) lineEdit_batchSize lineChanged(PreviousScaleInfo) diff --git a/ui/pitchDialog.ui b/ui/pitchDialog.ui index 6dafe6c5..48531316 100644 --- a/ui/pitchDialog.ui +++ b/ui/pitchDialog.ui @@ -32,7 +32,7 @@ Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing
- + Qt::CustomContextMenu @@ -45,7 +45,7 @@ - + 0 @@ -67,7 +67,7 @@ - + Qt::CustomContextMenu @@ -80,7 +80,7 @@ - + 0 @@ -136,7 +136,7 @@ - + Qt::CustomContextMenu @@ -334,7 +334,7 @@ - + 100 @@ -360,7 +360,7 @@ - + 100 @@ -376,7 +376,7 @@ - + Qt::CustomContextMenu @@ -389,7 +389,7 @@ - + 100 @@ -408,7 +408,7 @@ - + Qt::CustomContextMenu @@ -421,7 +421,7 @@ - + 100 @@ -448,66 +448,24 @@ - BtVolumeLabel + SmartLabel QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
- - BtDensityLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
- - BtVolumeEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtDensityEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtDateLabel - QLabel -
BtLabel.h
-
- - BtMassLabel - QLabel -
BtLabel.h
+
widgets/SmartLabel.h
changedSystemOfMeasurementOrScale(PreviousScaleInfo) popContextMenu(QPoint)
- BtMassEdit + SmartLineEdit QLineEdit -
BtAmountEdit.h
+
widgets/SmartLineEdit.h
textModified() lineChanged() lineChanged(PreviousScaleInfo)
- - BtGenericEdit - QLineEdit -
BtLineEdit.h
-
lineEdit_vol diff --git a/ui/recipeExtrasWidget.ui b/ui/recipeExtrasWidget.ui index 5c3ea5ce..c0b53381 100644 --- a/ui/recipeExtrasWidget.ui +++ b/ui/recipeExtrasWidget.ui @@ -67,9 +67,6 @@ 16777215
- - brewer -
@@ -128,13 +125,10 @@ 16777215
- - asstBrewer -
- + Qt::CustomContextMenu @@ -166,9 +160,6 @@ 16777215
- - primaryAge_days -
@@ -204,13 +195,10 @@ 16777215
- - primaryTemp_c -
- + Qt::CustomContextMenu @@ -242,9 +230,6 @@ 16777215
- - secondaryAge_days -
@@ -280,13 +265,10 @@ 16777215
- - secondaryTemp_c -
- + Qt::CustomContextMenu @@ -318,9 +300,6 @@ 16777215
- - tertiaryAge_days -
@@ -356,13 +335,10 @@ 16777215
- - tertiaryTemp_c -
- + Qt::CustomContextMenu @@ -394,9 +370,6 @@ 16777215
- - age -
@@ -432,9 +405,6 @@ 16777215 - - ageTemp_c -
@@ -506,9 +476,6 @@ true - - carbonation_vols - diff --git a/ui/strikeWaterDialog.ui b/ui/strikeWaterDialog.ui index 0303a527..f53ad939 100644 --- a/ui/strikeWaterDialog.ui +++ b/ui/strikeWaterDialog.ui @@ -38,7 +38,7 @@ QFormLayout::AllNonFixedFieldsGrow - + Qt::CustomContextMenu @@ -51,7 +51,7 @@ - + 0 @@ -64,7 +64,7 @@ - + Qt::CustomContextMenu @@ -77,7 +77,7 @@ - + 0 @@ -90,7 +90,7 @@ - + Qt::CustomContextMenu @@ -103,7 +103,7 @@ - + 0 @@ -119,7 +119,7 @@ - + Qt::CustomContextMenu @@ -132,7 +132,7 @@ - + 0 @@ -160,7 +160,7 @@ QFormLayout::AllNonFixedFieldsGrow - + Qt::CustomContextMenu @@ -173,7 +173,7 @@ - + 0 @@ -186,7 +186,7 @@ - + Qt::CustomContextMenu @@ -199,7 +199,7 @@ - + 0 @@ -212,7 +212,7 @@ - + Qt::CustomContextMenu @@ -225,7 +225,7 @@ - + 0 @@ -238,7 +238,7 @@ - + Qt::CustomContextMenu @@ -251,7 +251,7 @@ - + 0 @@ -264,7 +264,7 @@ - + Qt::CustomContextMenu @@ -277,7 +277,7 @@ - + 0 @@ -331,7 +331,7 @@ - + 0 @@ -356,7 +356,7 @@ - + 0 @@ -376,7 +376,7 @@ - + 0 @@ -401,7 +401,7 @@ - + 0 @@ -437,56 +437,24 @@ - BtVolumeLabel + SmartLabel QLabel -
BtLabel.h
+
widgets/SmartLabel.h
changedSystemOfMeasurementOrScale(PreviousScaleInfo) + popContextMenu(QPoint)
- BtVolumeEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtMassEdit + SmartLineEdit QLineEdit -
BtAmountEdit.h
+
widgets/SmartLineEdit.h
textModified() lineChanged() lineChanged(PreviousScaleInfo)
- - BtMassLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - popContextMenu(QPoint) - -
- - BtTemperatureLabel - QLabel -
BtLabel.h
- - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - -
- - BtTemperatureEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
diff --git a/ui/waterDialog.ui b/ui/waterDialog.ui index 24b3cecf..97095d1f 100644 --- a/ui/waterDialog.ui +++ b/ui/waterDialog.ui @@ -163,7 +163,7 @@
- + nappm @@ -177,7 +177,7 @@ - + so4ppm @@ -191,21 +191,21 @@ - + cappm - + ph - + hco3ppm @@ -240,14 +240,14 @@ - + mgppm - + clppm @@ -273,7 +273,7 @@
- + Qt::CustomContextMenu @@ -286,14 +286,14 @@ - + cacl - + Qt::CustomContextMenu @@ -306,14 +306,14 @@ - + mgso4 - + Qt::CustomContextMenu @@ -326,14 +326,14 @@ - + caco3 - + Qt::CustomContextMenu @@ -346,14 +346,14 @@ - + nacl - + Qt::CustomContextMenu @@ -366,14 +366,14 @@ - + caso4 - + Qt::CustomContextMenu @@ -386,7 +386,7 @@ - + nahco3 @@ -504,22 +504,17 @@
WaterButton.h
- BtMassLabel + SmartLabel QLabel -
BtLabel.h
+
widgets/SmartLabel.h
changedSystemOfMeasurementOrScale(PreviousScaleInfo)
- BtGenericDigit + SmartDigitWidget QLabel -
widgets/BtDigitWidget.h
-
- - BtMassDigit - QLabel -
widgets/BtAmountDigitWidget.h
+
widgets/SmartDigitWidget.h
diff --git a/ui/waterEditor.ui b/ui/waterEditor.ui index c31d0d30..9a91eb4b 100644 --- a/ui/waterEditor.ui +++ b/ui/waterEditor.ui @@ -6,7 +6,7 @@ 0 0 - 1071 + 1287 518 @@ -86,7 +86,7 @@
- + 0 @@ -124,7 +124,7 @@ - + 0 @@ -162,7 +162,7 @@ - + 0 @@ -203,7 +203,7 @@ - + 0 @@ -241,7 +241,7 @@ - + 0 @@ -263,27 +263,26 @@ - + - + 0 0 - - - HCO3 - - - - - CaCO3 - - + + Alkalinity + + + Qt::RichText + + + Qt::AlignLeading + - + 0 @@ -321,7 +320,7 @@ - + 0 @@ -342,6 +341,45 @@ + + + + + 0 + 0 + + + + Alkalinity measured as + + + Qt::RichText + + + Qt::AlignLeading + + + + + + + + 0 + 0 + + + + + HCO3 + + + + + CaCO3 + + + +
@@ -387,19 +425,9 @@
- BtAcidityEdit - QLineEdit -
BtAmountEdit.h
-
- - BtVolumeConcentrationEdit - QLineEdit -
BtAmountEdit.h
-
- - BtGenericEdit + SmartLineEdit QLineEdit -
BtLineEdit.h
+
widgets/SmartLineEdit.h
RadarChart @@ -414,7 +442,6 @@ lineEdit_na lineEdit_cl lineEdit_so4 - comboBox_alk lineEdit_alk lineEdit_ph plainTextEdit_notes diff --git a/ui/yeastEditor.ui b/ui/yeastEditor.ui index c148aab7..cf5a0faf 100644 --- a/ui/yeastEditor.ui +++ b/ui/yeastEditor.ui @@ -44,7 +44,7 @@
- + 0 @@ -63,7 +63,7 @@ - + Lab @@ -89,7 +89,7 @@ - + 0 @@ -166,7 +166,7 @@ - + 0 @@ -235,7 +235,7 @@ - + 0 @@ -359,7 +359,7 @@ - + 0 @@ -381,7 +381,7 @@ - + 0 @@ -403,7 +403,7 @@ - + Qt::CustomContextMenu @@ -442,7 +442,7 @@ - + Qt::CustomContextMenu @@ -455,7 +455,7 @@ - + 0 @@ -487,7 +487,7 @@ - + 0 @@ -516,7 +516,7 @@ - + 0 @@ -621,35 +621,20 @@ - BtPercentageEdit - QLineEdit -
BtLineEdit.h
-
- - BtGenericEdit - QLineEdit -
BtLineEdit.h
-
- - BtTemperatureEdit - QLineEdit -
BtAmountEdit.h
- - lineChanged(PreviousScaleInfo) - -
- - BtTemperatureLabel + SmartLabel QLabel -
BtLabel.h
+
widgets/SmartLabel.h
changedSystemOfMeasurementOrScale(PreviousScaleInfo)
- BtStringEdit + SmartLineEdit QLineEdit -
BtLineEdit.h
+
widgets/SmartLineEdit.h
+ + lineChanged(PreviousScaleInfo) +
From fe71626dfb8e93d3e9f3731c01486b1c7a7294bc Mon Sep 17 00:00:00 2001 From: Matt Young Date: Wed, 26 Apr 2023 21:06:17 +0200 Subject: [PATCH 20/42] Seems to run now --- src/AlcoholTool.cpp | 21 +++++----- src/BrewDayFormatter.cpp | 12 +++--- src/BrewDayScrollWidget.cpp | 6 +-- src/BrewNoteWidget.cpp | 2 +- src/HydrometerTool.cpp | 5 ++- src/MainWindow.cpp | 59 ++++++++++++++++---------- src/MashDesigner.cpp | 7 ++-- src/OgAdjuster.cpp | 4 +- src/RecipeFormatter.cpp | 20 ++++----- src/RefractoDialog.cpp | 25 ++++------- src/SmartAmounts.cpp | 18 ++++---- src/SmartAmounts.h | 28 ++++++------- src/SmartField.cpp | 11 +++-- src/SmartField.h | 9 ++++ src/measurement/Unit.cpp | 6 +-- src/measurement/Unit.h | 2 +- src/measurement/UnitSystem.cpp | 2 +- src/model/NamedEntity.cpp | 2 +- src/model/Recipe.cpp | 2 +- src/unitTests/Testing.cpp | 2 +- src/widgets/SmartLabel.cpp | 28 ++++++++++--- src/widgets/SmartLabel.h | 23 ++++++++++ ui/mainWindow.ui | 76 +++------------------------------- ui/waterEditor.ui | 22 ++++++---- 24 files changed, 200 insertions(+), 192 deletions(-) diff --git a/src/AlcoholTool.cpp b/src/AlcoholTool.cpp index 5835cd8a..f94e72e0 100644 --- a/src/AlcoholTool.cpp +++ b/src/AlcoholTool.cpp @@ -66,10 +66,6 @@ class AlcoholTool::impl { label_result {new QLabel (&self)}, output_result {new QLabel (&self)}, gridLayout {new QGridLayout (&self)} { - this->restoreSettings(); - this->enableAdvancedInputs->setFont(QFont("Roboto medium", 13)); - this->output_result->setText("%"); - this->doLayout(); SMART_FIELD_INIT_FS(AlcoholTool, label_og , input_og , double, Measurement::PhysicalQuantity::Density ); SMART_FIELD_INIT_FS(AlcoholTool, label_fg , input_fg , double, Measurement::PhysicalQuantity::Density ); @@ -77,6 +73,11 @@ class AlcoholTool::impl { SMART_FIELD_INIT_FS(AlcoholTool, label_temperature , input_fg_temperature , double, Measurement::PhysicalQuantity::Temperature); SMART_FIELD_INIT_FS(AlcoholTool, label_calibration_temperature, input_calibration_temperature, double, Measurement::PhysicalQuantity::Temperature); + this->restoreSettings(); + this->enableAdvancedInputs->setFont(QFont("Roboto medium", 13)); + this->output_result->setText("%"); + this->doLayout(); + this->connectSignals(); return; } @@ -134,12 +135,12 @@ class AlcoholTool::impl { void showOrHideAdvancedControls() { bool visible = this->enableAdvancedInputs->isChecked(); - this->label_temperature->setVisible(visible); - this->label_corrected->setVisible(visible); - this->input_og_temperature->setVisible(visible); - this->corrected_og->setVisible(visible); - this->input_fg_temperature->setVisible(visible); - this->corrected_fg->setVisible(visible); + this->label_temperature ->setVisible(visible); + this->label_corrected ->setVisible(visible); + this->input_og_temperature ->setVisible(visible); + this->corrected_og ->setVisible(visible); + this->input_fg_temperature ->setVisible(visible); + this->corrected_fg ->setVisible(visible); this->label_calibration_temperature->setVisible(visible); this->input_calibration_temperature->setVisible(visible); diff --git a/src/BrewDayFormatter.cpp b/src/BrewDayFormatter.cpp index 7cb19609..03addb37 100644 --- a/src/BrewDayFormatter.cpp +++ b/src/BrewDayFormatter.cpp @@ -84,21 +84,21 @@ QString BrewDayFormatter::buildTitleHtml(bool includeImage) { .arg(tr("Boil Volume")) .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilVolume_l(), Measurement::Units::liters}, 2)) .arg(tr("Preboil Gravity")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilGrav(), Measurement::Units::sp_grav}, 3)); + .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilGrav(), Measurement::Units::specificGravity}, 3)); // fourth row: Final volume and starting gravity body += QString("%1%2%3%4") .arg(tr("Final Volume")) .arg(Measurement::displayAmount(Measurement::Amount{recObs->finalVolume_l(), Measurement::Units::liters}, 2)) .arg(tr("Starting Gravity")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->og(), Measurement::Units::sp_grav}, 3)); + .arg(Measurement::displayAmount(Measurement::Amount{recObs->og(), Measurement::Units::specificGravity}, 3)); // fifth row: IBU and Final gravity body += QString("%1%2%3%4") .arg(tr("IBU")) .arg(Measurement::displayQuantity(recObs->IBU(), 1)) .arg(tr("Final Gravity")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->fg(), Measurement::Units::sp_grav}, 3)); + .arg(Measurement::displayAmount(Measurement::Amount{recObs->fg(), Measurement::Units::specificGravity}, 3)); // sixth row: ABV and estimate calories bool metricVolume = @@ -147,7 +147,7 @@ QList BrewDayFormatter::buildTitleList() { row.append(tr("Boil Volume")); row.append(Measurement::displayAmount(Measurement::Amount{recObs->boilVolume_l(), Measurement::Units::liters}, 2)); row.append(tr("Preboil Gravity")); - row.append(Measurement::displayAmount(Measurement::Amount{recObs->boilGrav(), Measurement::Units::sp_grav}, 3)); + row.append(Measurement::displayAmount(Measurement::Amount{recObs->boilGrav(), Measurement::Units::specificGravity}, 3)); ret.append(row); row.clear(); ret.append(row); @@ -157,7 +157,7 @@ QList BrewDayFormatter::buildTitleList() { row.append(tr("Final Volume")); row.append(Measurement::displayAmount(Measurement::Amount{recObs->finalVolume_l(), Measurement::Units::liters}, 2)); row.append(tr("Starting Gravity")); - row.append(Measurement::displayAmount(Measurement::Amount{recObs->og(), Measurement::Units::sp_grav}, 3)); + row.append(Measurement::displayAmount(Measurement::Amount{recObs->og(), Measurement::Units::specificGravity}, 3)); ret.append(row); row.clear(); @@ -165,7 +165,7 @@ QList BrewDayFormatter::buildTitleList() { row.append(tr("IBU")); row.append(Measurement::displayQuantity(recObs->IBU(), 1)); row.append(tr("Final Gravity")); - row.append(Measurement::displayAmount(Measurement::Amount{recObs->fg(), Measurement::Units::sp_grav}, 3)); + row.append(Measurement::displayAmount(Measurement::Amount{recObs->fg(), Measurement::Units::specificGravity}, 3)); ret.append(row); row.clear(); diff --git a/src/BrewDayScrollWidget.cpp b/src/BrewDayScrollWidget.cpp index 55082ab0..bd8b75cf 100644 --- a/src/BrewDayScrollWidget.cpp +++ b/src/BrewDayScrollWidget.cpp @@ -376,21 +376,21 @@ QString BrewDayScrollWidget::buildTitleTable(bool includeImage) { .arg(tr("Boil Volume")) .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilVolume_l(), Measurement::Units::liters}, 2)) .arg(tr("Preboil Gravity")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilGrav(), Measurement::Units::sp_grav}, 3)); + .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilGrav(), Measurement::Units::specificGravity}, 3)); // fourth row: Final volume and starting gravity body += QString("%1%2%3%4") .arg(tr("Final Volume")) .arg(Measurement::displayAmount(Measurement::Amount{recObs->finalVolume_l(), Measurement::Units::liters}, 2)) .arg(tr("Starting Gravity")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->og(), Measurement::Units::sp_grav}, 3)); + .arg(Measurement::displayAmount(Measurement::Amount{recObs->og(), Measurement::Units::specificGravity}, 3)); // fifth row: IBU and Final gravity body += QString("%1%2%3%4") .arg(tr("IBU")) .arg( Measurement::displayQuantity(recObs->IBU(), 1)) .arg(tr("Final Gravity")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->fg(), Measurement::Units::sp_grav}, 3)); + .arg(Measurement::displayAmount(Measurement::Amount{recObs->fg(), Measurement::Units::specificGravity}, 3)); // sixth row: ABV and estimate calories bool metricVolume = ( diff --git a/src/BrewNoteWidget.cpp b/src/BrewNoteWidget.cpp index cc312514..4cea650a 100644 --- a/src/BrewNoteWidget.cpp +++ b/src/BrewNoteWidget.cpp @@ -99,7 +99,7 @@ void BrewNoteWidget::updateDateFormat() { void BrewNoteWidget::updateProjOg() { // Density UnitSystems only have one scale, so we don't bother looking up UnitSystem::RelativeScale auto forcedSystemOfMeasurement = this->label_projectedOg->getForcedSystemOfMeasurement(); - double quant = Measurement::amountDisplay(Measurement::Amount{this->bNoteObs->projOg(), Measurement::Units::sp_grav}, + double quant = Measurement::amountDisplay(Measurement::Amount{this->bNoteObs->projOg(), Measurement::Units::specificGravity}, forcedSystemOfMeasurement); this->lcdnumber_projectedOG->setLowLim( lowLimitPct * quant); this->lcdnumber_projectedOG->setHighLim(highLimitPct * quant); diff --git a/src/HydrometerTool.cpp b/src/HydrometerTool.cpp index 5dc34aee..462790c4 100644 --- a/src/HydrometerTool.cpp +++ b/src/HydrometerTool.cpp @@ -40,6 +40,9 @@ HydrometerTool::HydrometerTool(QWidget* parent) : QDialog(parent) { SMART_FIELD_INIT_FS(HydrometerTool, label_calibratedTemp, lineEdit_calibratedTemp, double, Measurement::PhysicalQuantity::Temperature, 1); SMART_FIELD_INIT_FS(HydrometerTool, label_inputTemp , lineEdit_inputTemp , double, Measurement::PhysicalQuantity::Temperature); + this->lineEdit_calibratedTemp->setAmount(15.55555556); +/// lineEdit_outputSg->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); + connect(this->pushButton_convert, &QAbstractButton::clicked, this, &HydrometerTool::convert ); connect(this->label_inputTemp, &SmartLabel::changedSystemOfMeasurementOrScale, this->lineEdit_inputTemp, &SmartLineEdit::lineChanged); connect(this->label_inputSg, &SmartLabel::changedSystemOfMeasurementOrScale, this->lineEdit_inputSg, &SmartLineEdit::lineChanged); @@ -82,7 +85,6 @@ void HydrometerTool::doLayout() { lineEdit_calibratedTemp->setMinimumSize(QSize(80, 0)); lineEdit_calibratedTemp->setMaximumSize(QSize(80, 16777215)); lineEdit_calibratedTemp->setObjectName(QStringLiteral("lineEdit_calibratedTemp")); - lineEdit_calibratedTemp->setAmount(15.55555556); label_outputSg = new SmartLabel(groupBox_inputSg); label_outputSg ->setContextMenuPolicy(Qt::CustomContextMenu); @@ -90,7 +92,6 @@ void HydrometerTool::doLayout() { lineEdit_outputSg = new SmartLineEdit(groupBox_inputSg); lineEdit_outputSg->setMinimumSize(QSize(80, 0)); lineEdit_outputSg->setMaximumSize(QSize(80, 16777215)); -/// lineEdit_outputSg->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); lineEdit_outputSg->setReadOnly(true); diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index e6041d6f..3b412916 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -185,6 +185,7 @@ namespace { double const minCanonicalValue, double const maxCanonicalValue, double const maxPossibleCanonicalValue) { + qDebug() << Q_FUNC_INFO << "label" << &label; slider.setPreferredRange(label.getRangeToDisplay(minCanonicalValue, maxCanonicalValue )); slider.setRange (label.getRangeToDisplay(1.000 , maxPossibleCanonicalValue)); @@ -243,11 +244,27 @@ class MainWindow::impl { MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), pimpl{std::make_unique(*this)} { qDebug() << Q_FUNC_INFO; - undoStack = new QUndoStack(this); + this->undoStack = new QUndoStack(this); // Need to call this parent class method to get all the widgets added (I think). this->setupUi(this); + // Initialise smart labels etc early, but after call to this->setupUi() because otherwise member variables such as + // label_name will not yet be set. + // .:TBD:. We should fix some of these inconsistently-named labels + SMART_FIELD_INIT(MainWindow, label_name , lineEdit_name , Recipe, PropertyNames::NamedEntity::name ); + SMART_FIELD_INIT(MainWindow, label_targetBatchSize, lineEdit_batchSize , Recipe, PropertyNames::Recipe::batchSize_l , 2); + SMART_FIELD_INIT(MainWindow, label_targetBoilSize , lineEdit_boilSize , Recipe, PropertyNames::Recipe::boilSize_l , 2); + SMART_FIELD_INIT(MainWindow, label_efficiency , lineEdit_efficiency, Recipe, PropertyNames::Recipe::efficiency_pct, 1); + SMART_FIELD_INIT(MainWindow, label_boilTime , lineEdit_boilTime , Recipe, PropertyNames::Recipe::boilTime_min , 1); + SMART_FIELD_INIT(MainWindow, label_boilSg , lineEdit_boilSg , Recipe, PropertyNames::Recipe::boilGrav , 3); + + SMART_FIELD_INIT_NO_SF(MainWindow, oGLabel , Recipe, PropertyNames::Recipe::og ); + SMART_FIELD_INIT_NO_SF(MainWindow, fGLabel , Recipe, PropertyNames::Recipe::fg ); + SMART_FIELD_INIT_NO_SF(MainWindow, colorSRMLabel , Recipe, PropertyNames::Recipe::color_srm ); + SMART_FIELD_INIT_NO_SF(MainWindow, label_batchSize, Recipe, PropertyNames::Recipe::boilSize_l); + SMART_FIELD_INIT_NO_SF(MainWindow, label_boilSize , Recipe, PropertyNames::Recipe::boilSize_l); + // Stop things looking ridiculously tiny on high DPI displays this->setSizesInPixelsBasedOnDpi(); @@ -280,11 +297,14 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), pimpl{std::make_u #else printer->setPageSize(QPageSize(QPageSize::Letter)); #endif + return; } void MainWindow::init() { qDebug() << Q_FUNC_INFO; + + this->setupCSS(); // initialize all of the dialog windows this->setupDialogs(); @@ -301,11 +321,6 @@ void MainWindow::init() { // do all the work for checkboxes (just one right now) this->setUpStateChanges(); - // This sets up things that might have been 'remembered' (ie stored in the config file) from a previous run of the - // program - eg window size, which is stored in MainWindow::closeEvent(). - // Breaks the naming convention, doesn't it? - this->restoreSavedState(); - // Connect menu item slots to triggered() signals this->setupTriggers(); // Connect pushbutton slots to clicked() signals @@ -319,19 +334,17 @@ void MainWindow::init() { // set up the drag/drop parts this->setupDrops(); + // This sets up things that might have been 'remembered' (ie stored in the config file) from a previous run of the + // program - eg window size, which is stored in MainWindow::closeEvent(). + // Breaks the naming convention, doesn't it? + this->restoreSavedState(); + // Moved from Database class Recipe::connectSignalsForAllRecipes(); qDebug() << Q_FUNC_INFO << "Recipe signals connected"; Mash::connectSignals(); qDebug() << Q_FUNC_INFO << "Mash signals connected"; - // .:TBD:. We should fix these inconsistently-named labels - SMART_FIELD_INIT(MainWindow, label_boilSg , lineEdit_boilSg , Recipe, PropertyNames::Recipe::boilGrav ); - SMART_FIELD_INIT(MainWindow, label_boiltime , lineEdit_boilTime , Recipe, PropertyNames::Recipe::boilTime_min ); - SMART_FIELD_INIT(MainWindow, efficiencyLabel , lineEdit_efficiency, Recipe, PropertyNames::Recipe::efficiency_pct); - SMART_FIELD_INIT(MainWindow, label_targetBatchSize, lineEdit_batchSize , Recipe, PropertyNames::Recipe::batchSize_l ); - SMART_FIELD_INIT(MainWindow, label_boilsize , lineEdit_boilSize , Recipe, PropertyNames::Recipe::boilSize_l ); - // I do not like this connection here. connect(this->ancestorDialog, &AncestorDialog::ancestoryChanged, treeView_recipe->model(), &BtTreeModel::versionedRecipe); connect(this->optionDialog, &OptionDialog::showAllAncestors, treeView_recipe->model(), &BtTreeModel::catchAncestors ); @@ -886,10 +899,10 @@ void MainWindow::setupActivate() { // lineEdits with either an editingFinished() or a textModified() should go in // here void MainWindow::setupTextEdit() { - connect(this->lineEdit_name, &QLineEdit::editingFinished, this, &MainWindow::updateRecipeName); - connect(this->lineEdit_batchSize, &SmartLineEdit::textModified, this, &MainWindow::updateRecipeBatchSize); - connect(this->lineEdit_boilSize, &SmartLineEdit::textModified, this, &MainWindow::updateRecipeBoilSize); - connect(this->lineEdit_boilTime, &SmartLineEdit::textModified, this, &MainWindow::updateRecipeBoilTime); + connect(this->lineEdit_name , &QLineEdit::editingFinished, this, &MainWindow::updateRecipeName); + connect(this->lineEdit_batchSize , &SmartLineEdit::textModified, this, &MainWindow::updateRecipeBatchSize); + connect(this->lineEdit_boilSize , &SmartLineEdit::textModified, this, &MainWindow::updateRecipeBoilSize); + connect(this->lineEdit_boilTime , &SmartLineEdit::textModified, this, &MainWindow::updateRecipeBoilTime); connect(this->lineEdit_efficiency, &SmartLineEdit::textModified, this, &MainWindow::updateRecipeEfficiency); return; } @@ -1378,10 +1391,10 @@ void MainWindow::showChanges(QMetaProperty* prop) { Style const * style = this->recipeObs->style(); - updateDensitySlider(*this->styleRangeWidget_og, this->oGLabel, style->ogMin(), style->ogMax(), 1.120); + updateDensitySlider(*this->styleRangeWidget_og, *this->oGLabel, style->ogMin(), style->ogMax(), 1.120); this->styleRangeWidget_og->setValue(this->oGLabel->getAmountToDisplay(recipeObs->og())); - updateDensitySlider(*this->styleRangeWidget_fg, this->fGLabel, style->fgMin(), style->fgMax(), 1.030); + updateDensitySlider(*this->styleRangeWidget_fg, *this->fGLabel, style->fgMin(), style->fgMax(), 1.030); this->styleRangeWidget_fg->setValue(this->fGLabel->getAmountToDisplay(recipeObs->fg())); this->styleRangeWidget_abv->setValue(recipeObs->ABV_pct()); @@ -1394,14 +1407,14 @@ void MainWindow::showChanges(QMetaProperty* prop) { this->rangeWidget_batchSize->setValue (this->label_batchSize->getAmountToDisplay(this->recipeObs->finalVolume_l())); this->rangeWidget_boilsize->setRange (0, - this->label_boilsize->getAmountToDisplay(this->recipeObs->boilSize_l())); + this->label_boilSize->getAmountToDisplay(this->recipeObs->boilSize_l())); this->rangeWidget_boilsize->setPreferredRange(0, - this->label_boilsize->getAmountToDisplay(this->recipeObs->boilVolume_l())); - this->rangeWidget_boilsize->setValue (this->label_boilsize->getAmountToDisplay(this->recipeObs->boilVolume_l())); + this->label_boilSize->getAmountToDisplay(this->recipeObs->boilVolume_l())); + this->rangeWidget_boilsize->setValue (this->label_boilSize->getAmountToDisplay(this->recipeObs->boilVolume_l())); /* Colors need the same basic treatment as gravity */ updateColorSlider(*this->styleRangeWidget_srm, - this->colorSRMLabel, + *this->colorSRMLabel, style->colorMin_srm(), style->colorMax_srm()); this->styleRangeWidget_srm->setValue(this->colorSRMLabel->getAmountToDisplay(this->recipeObs->color_srm())); diff --git a/src/MashDesigner.cpp b/src/MashDesigner.cpp index 16452e1f..7e5475e2 100644 --- a/src/MashDesigner.cpp +++ b/src/MashDesigner.cpp @@ -41,12 +41,13 @@ MashDesigner::MashDesigner(QWidget * parent) : QDialog {parent}, addedWater_l{0} { this->setupUi(this); + // .:TODO:. Would be good to make the label & field naming a bit more consistent in the .ui file + SMART_FIELD_INIT_FS(MashDesigner, label_targetTemp, lineEdit_temp, double, Measurement::PhysicalQuantity::Temperature, 1); // Target temp. + SMART_FIELD_INIT_FS(MashDesigner, label_stepTime, lineEdit_time, double, Measurement::PhysicalQuantity::Time, 0); // Time + this->label_zeroVol ->setText(Measurement::displayAmount(Measurement::Amount{0, Measurement::Units::liters})); this->label_zeroWort->setText(Measurement::displayAmount(Measurement::Amount{0, Measurement::Units::liters})); - SMART_FIELD_INIT_FS(MashDesigner, label_temp, lineEdit_temp, double, Measurement::PhysicalQuantity::Temperature, 1); // Target temp. - SMART_FIELD_INIT_FS(MashDesigner, label_stepTime, lineEdit_time, double, Measurement::PhysicalQuantity::Time, 0); // Time - // Update temp slider when we move amount slider. connect(horizontalSlider_amount, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateTempSlider); // Update amount slider when we move temp slider. diff --git a/src/OgAdjuster.cpp b/src/OgAdjuster.cpp index adf21455..a202aab0 100644 --- a/src/OgAdjuster.cpp +++ b/src/OgAdjuster.cpp @@ -31,12 +31,12 @@ OgAdjuster::OgAdjuster( QWidget* parent ) : recObs {nullptr} { setupUi(this); - SMART_FIELD_INIT_FIXED(OgAdjuster, label_sg , lineEdit_sg , double, Measurement::Units::sp_grav , 3); // Input: SG + SMART_FIELD_INIT_FIXED(OgAdjuster, label_sg , lineEdit_sg , double, Measurement::Units::specificGravity , 3); // Input: SG SMART_FIELD_INIT_FS (OgAdjuster, label_temp , lineEdit_temp , double, Measurement::PhysicalQuantity::Temperature, 1); // Input: Temp SMART_FIELD_INIT_FS (OgAdjuster, label_calTemp , lineEdit_calTemp , double, Measurement::PhysicalQuantity::Temperature, 1); // Input: Calibration Temp SMART_FIELD_INIT_FIXED(OgAdjuster, label_plato , lineEdit_plato , double, Measurement::Units::plato , 1); // Input: Plato SMART_FIELD_INIT_FS (OgAdjuster, label_volume , lineEdit_volume , double, Measurement::PhysicalQuantity::Volume ); // Input: Pre-Boil Volume - SMART_FIELD_INIT_FIXED(OgAdjuster, label_og , lineEdit_og , double, Measurement::Units::sp_grav , 3); // Output: OG w/o Correction + SMART_FIELD_INIT_FIXED(OgAdjuster, label_og , lineEdit_og , double, Measurement::Units::specificGravity , 3); // Output: OG w/o Correction SMART_FIELD_INIT_FS (OgAdjuster, label_add , lineEdit_add , double, Measurement::PhysicalQuantity::Volume ); // Output: Add to Boil SMART_FIELD_INIT_FS (OgAdjuster, label_batchSize, lineEdit_batchSize, double, Measurement::PhysicalQuantity::Volume ); // Output: Final Batch Size diff --git a/src/RecipeFormatter.cpp b/src/RecipeFormatter.cpp index ae1ad734..76cb8a2f 100644 --- a/src/RecipeFormatter.cpp +++ b/src/RecipeFormatter.cpp @@ -319,11 +319,11 @@ class RecipeFormatter::impl { "%1" "%2") .arg(tr("OG")) - .arg(Measurement::displayAmount(Measurement::Amount{rec->og(), Measurement::Units::sp_grav}, 3)); + .arg(Measurement::displayAmount(Measurement::Amount{rec->og(), Measurement::Units::specificGravity}, 3)); body += QString("%1" "%2") .arg(tr("FG")) - .arg(Measurement::displayAmount(Measurement::Amount{rec->fg(), Measurement::Units::sp_grav}, 3)); + .arg(Measurement::displayAmount(Measurement::Amount{rec->fg(), Measurement::Units::specificGravity}, 3)); // Fourth row: ABV and Bitterness. We need to set the bitterness string up first body += QString("" @@ -385,10 +385,10 @@ class RecipeFormatter::impl { value.append(QString("%1%").arg(rec->efficiency_pct(), 0, 'f', 0)); entry.append(tr("OG")); value.append(QString("%1").arg(Measurement::displayAmount(Measurement::Amount{rec->og(), - Measurement::Units::sp_grav}, 3))); + Measurement::Units::specificGravity}, 3))); entry.append(tr("FG")); value.append(QString("%1").arg(Measurement::displayAmount(Measurement::Amount{rec->fg(), - Measurement::Units::sp_grav}, 3))); + Measurement::Units::specificGravity}, 3))); entry.append(tr("ABV")); value.append(QString("%1%").arg(Measurement::displayQuantity(rec->ABV_pct(), 1))); entry.append(tr("Bitterness")); @@ -1003,7 +1003,7 @@ class RecipeFormatter::impl { bnTable += QString("%1").arg(tr("Preboil")); bnTable += QString("%1%2%3%4") .arg(tr("SG")) - .arg(Measurement::displayAmount(Measurement::Amount{note->sg(), Measurement::Units::sp_grav}, 3)) + .arg(Measurement::displayAmount(Measurement::Amount{note->sg(), Measurement::Units::specificGravity}, 3)) .arg(tr("Volume into BK")) .arg(Measurement::displayAmount(Measurement::Amount{note->volumeIntoBK_l(), Measurement::Units::liters})); @@ -1021,7 +1021,7 @@ class RecipeFormatter::impl { .arg(Measurement::displayQuantity(note->calculateEffIntoBK_pct(), 2)) .arg(tr("Projected OG")) .arg(Measurement::displayAmount(Measurement::Amount{note->calculateOg(), - Measurement::Units::sp_grav}, 3)); + Measurement::Units::specificGravity}, 3)); bnTable += ""; // POSTBOIL @@ -1029,7 +1029,7 @@ class RecipeFormatter::impl { bnTable += QString("%1").arg(tr("Postboil")); bnTable += QString("%1%2%3%4") .arg(tr("OG")) - .arg(Measurement::displayAmount(Measurement::Amount{note->og(), Measurement::Units::sp_grav}, 3)) + .arg(Measurement::displayAmount(Measurement::Amount{note->og(), Measurement::Units::specificGravity}, 3)) .arg(tr("Postboil Volume")) .arg(Measurement::displayAmount(Measurement::Amount{note->postBoilVolume_l(), Measurement::Units::liters})); @@ -1050,7 +1050,7 @@ class RecipeFormatter::impl { bnTable += QString("%1").arg(tr("Postferment")); bnTable += QString("%1%2%3%4") .arg(tr("FG")) - .arg(Measurement::displayAmount(Measurement::Amount{note->fg(), Measurement::Units::sp_grav}, 3)) + .arg(Measurement::displayAmount(Measurement::Amount{note->fg(), Measurement::Units::specificGravity}, 3)) .arg(tr("Volume")) .arg(Measurement::displayAmount(Measurement::Amount{note->finalVolume_l(), Measurement::Units::liters})); @@ -1226,10 +1226,10 @@ QString RecipeFormatter::getToolTip(Recipe* rec) { // Third row: OG and FG body += QString("%1%2") .arg(tr("OG")) - .arg(Measurement::displayAmount(Measurement::Amount{rec->og(), Measurement::Units::sp_grav}, 3)); + .arg(Measurement::displayAmount(Measurement::Amount{rec->og(), Measurement::Units::specificGravity}, 3)); body += QString("%1%2") .arg(tr("FG")) - .arg(Measurement::displayAmount(Measurement::Amount{rec->fg(), Measurement::Units::sp_grav}, 3)); + .arg(Measurement::displayAmount(Measurement::Amount{rec->fg(), Measurement::Units::specificGravity}, 3)); // Fourth row: Color and Bitterness. body += QString("%1%2 (%3)") diff --git a/src/RefractoDialog.cpp b/src/RefractoDialog.cpp index eff18779..f2fe4709 100644 --- a/src/RefractoDialog.cpp +++ b/src/RefractoDialog.cpp @@ -33,22 +33,15 @@ RefractoDialog::RefractoDialog(QWidget* parent) : QDialog(parent) { setupUi(this); // Note that the labels here are QLabel, not SmartLabel, because we want the units fixed, not user-selectable - SMART_FIELD_INIT_FS(RefractoDialog, label_op , lineEdit_op , double, Measurement::PhysicalQuantity::Density , 1); // Original Plato - SMART_FIELD_INIT_FS(RefractoDialog, label_inputOG, lineEdit_inputOG, double, Measurement::PhysicalQuantity::Density , 3); // Original gravity in - SMART_FIELD_INIT_FS(RefractoDialog, label_cp , lineEdit_cp , double, Measurement::PhysicalQuantity::Density , 1); // Current Plato - SMART_FIELD_INIT_FS(RefractoDialog, label_ri , lineEdit_ri , double, NonPhysicalQuantity::Dimensionless ); // Refractive index - SMART_FIELD_INIT_FS(RefractoDialog, label_og , lineEdit_og , double, Measurement::PhysicalQuantity::Density , 3); // Original gravity out - SMART_FIELD_INIT_FS(RefractoDialog, label_sg , lineEdit_sg , double, Measurement::PhysicalQuantity::Density , 3); // Specific gravity out - SMART_FIELD_INIT_FS(RefractoDialog, label_abv , lineEdit_abv , double, NonPhysicalQuantity::Percentage ); // Alcohol by volume - SMART_FIELD_INIT_FS(RefractoDialog, label_abw , lineEdit_abw , double, NonPhysicalQuantity::Percentage ); // Alcohol by weight - SMART_FIELD_INIT_FS(RefractoDialog, label_re , lineEdit_re , double, Measurement::PhysicalQuantity::Density , 1); // Real extract Plato - - this->lineEdit_op ->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::Plato ); - this->lineEdit_inputOG->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); - this->lineEdit_cp ->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::Plato ); - this->lineEdit_og ->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); - this->lineEdit_sg ->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); - this->lineEdit_re ->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::Plato ); + SMART_FIELD_INIT_FIXED(RefractoDialog, label_op , lineEdit_op , double, Measurement::Units::plato , 1); // Original Plato + SMART_FIELD_INIT_FIXED(RefractoDialog, label_inputOG, lineEdit_inputOG, double, Measurement::Units::specificGravity, 3); // Original gravity in + SMART_FIELD_INIT_FIXED(RefractoDialog, label_cp , lineEdit_cp , double, Measurement::Units::plato , 1); // Current Plato + SMART_FIELD_INIT_FIXED(RefractoDialog, label_og , lineEdit_og , double, Measurement::Units::specificGravity, 3); // Original gravity out + SMART_FIELD_INIT_FIXED(RefractoDialog, label_sg , lineEdit_sg , double, Measurement::Units::specificGravity, 3); // Specific gravity out + SMART_FIELD_INIT_FIXED(RefractoDialog, label_re , lineEdit_re , double, Measurement::Units::plato , 1); // Real extract Plato + SMART_FIELD_INIT_FS (RefractoDialog, label_ri , lineEdit_ri , double, NonPhysicalQuantity::Dimensionless ); // Refractive index + SMART_FIELD_INIT_FS (RefractoDialog, label_abv , lineEdit_abv , double, NonPhysicalQuantity::Percentage ); // Alcohol by volume + SMART_FIELD_INIT_FS (RefractoDialog, label_abw , lineEdit_abw , double, NonPhysicalQuantity::Percentage ); // Alcohol by weight connect(this->pushButton_calculate, &QAbstractButton::clicked, this, &RefractoDialog::calculate ); return; diff --git a/src/SmartAmounts.cpp b/src/SmartAmounts.cpp index d5d8e782..235ea0c2 100644 --- a/src/SmartAmounts.cpp +++ b/src/SmartAmounts.cpp @@ -44,7 +44,7 @@ template<> void SmartAmounts::Init(char const * const editorName, QLabel & label, char const * const fieldName, char const * const fieldlFqName, - SmartField & field, + SmartField & field, TypeInfo const & typeInfo, std::optional const precision, QString const & maximalDisplayString) { @@ -52,13 +52,15 @@ template<> void SmartAmounts::Init(char const * const editorName, return; } -void SmartAmounts::InitNoSle(char const * const editorName, - char const * const labelName, - char const * const labelFqName, - SmartLabel & label, - TypeInfo const & typeInfo, - std::optional const precision, - QString const & maximalDisplayString) { +// .:TBD:. I think it is unnecessary to have precision and maximalDisplayString when there is no SmartField, but leaving +// them in for the moment, until I'm 100% sure.` +void SmartAmounts::InitNoSf(char const * const editorName, + char const * const labelName, + char const * const labelFqName, + SmartLabel & label, + TypeInfo const & typeInfo, + [[maybe_unused]] std::optional const precision, + [[maybe_unused]] QString const & maximalDisplayString) { label.init(editorName, labelName, labelFqName, nullptr, typeInfo); return; } diff --git a/src/SmartAmounts.h b/src/SmartAmounts.h index f0bd5daa..7ab7900d 100644 --- a/src/SmartAmounts.h +++ b/src/SmartAmounts.h @@ -52,13 +52,13 @@ namespace SmartAmounts { /** * \brief Alternate version of \c SmartAmounts::Init for when there is a \c SmartLabel but no \c SmartField */ - void InitNoSle(char const * const editorName, - char const * const labelName, - char const * const labelFqName, - SmartLabel & label, - TypeInfo const & typeInfo, - std::optional const precision = std::nullopt, - QString const & maximalDisplayString = "100.000 srm"); + void InitNoSf(char const * const editorName, + char const * const labelName, + char const * const labelFqName, + SmartLabel & label, + TypeInfo const & typeInfo, + std::optional const precision = std::nullopt, + QString const & maximalDisplayString = "100.000 srm"); /** * \brief Alternate version of \c SmartAmounts::Init for when units are fixed. (Note that this implies label is @@ -203,13 +203,13 @@ namespace SmartAmounts { /** * \brief Alternate version of \c SMART_FIELD_INIT for when there is no \c SmartField */ -#define SMART_FIELD_INIT_NO_SLE(editorClass, labelName, modelClass, propertyName, ...) \ - SmartAmounts::InitNoSle(#editorClass, \ - #labelName, \ - SFI_FQ_NAME(editorClass, labelName), \ - *this->labelName, \ - modelClass ::typeLookup.getType(propertyName) \ - __VA_OPT__(, __VA_ARGS__)) +#define SMART_FIELD_INIT_NO_SF(editorClass, labelName, modelClass, propertyName, ...) \ + SmartAmounts::InitNoSf(#editorClass, \ + #labelName, \ + SFI_FQ_NAME(editorClass, labelName), \ + *this->labelName, \ + modelClass ::typeLookup.getType(propertyName) \ + __VA_OPT__(, __VA_ARGS__)) /** * \brief An alternate version of \c SMART_FIELD_INIT for use when there is no \c modelClass (eg in a free-standing diff --git a/src/SmartField.cpp b/src/SmartField.cpp index bd606e07..286dea6f 100644 --- a/src/SmartField.cpp +++ b/src/SmartField.cpp @@ -29,6 +29,7 @@ #include #include "Localization.h" +#include "Logging.h" #include "measurement/Measurement.h" #include "utils/OptionalHelpers.h" #include "utils/TypeLookup.h" @@ -83,7 +84,7 @@ class SmartField::impl { if (precision) { // It's a coding error to specify precision for a field that's not a (possibly optional) double (or a float, // but we don't use float). However, we allow precision of 0 for a type that is stored as an int or unsigned - // int. + // int, because that's what we're going to set it to anyway. Q_ASSERT(this->m_typeInfo->typeIndex == typeid(double) || this->m_typeInfo->typeIndex == typeid(std::optional) || (0 == *precision && this->m_typeInfo->typeIndex == typeid(int )) || @@ -103,7 +104,6 @@ class SmartField::impl { this->m_precision = 0; } this->m_maximalDisplayString = maximalDisplayString; - this->m_initialised = true; if (std::holds_alternative(*this->m_typeInfo->fieldType)) { // It's a coding error to have either a smartBuddyLabel or a fixedDisplayUnit for a NonPhysicalQuantity @@ -124,6 +124,8 @@ class SmartField::impl { } } + this->m_initialised = true; + // Now let our subclass (SmartLineEdit, SmartDigitWidget, etc) do any of its own initialisation this->m_self.doPostInitWork(); @@ -238,7 +240,6 @@ template<> void SmartField::init(char const * const edi // It's a coding error to call this version of init with a PhysicalQuantity Q_ASSERT(typeInfo.fieldType && std::holds_alternative(*typeInfo.fieldType)); - this->pimpl->init(editorName, fieldName, fieldFqName, @@ -398,6 +399,10 @@ template void SmartField::setAmount(std::optional amount); template void SmartField::setAmount(std::optional amount); template void SmartField::setAmount(T amount) { + // Only need next bit for debugging! +// if (!this->pimpl->m_initialised) { +// qCritical().noquote() << Q_FUNC_INFO << this->pimpl->m_fieldFqName << "Stack trace:" << Logging::getStackTrace(); +// } Q_ASSERT(this->pimpl->m_initialised); qDebug() << Q_FUNC_INFO << this->pimpl->m_fieldFqName << "amount =" << amount; diff --git a/src/SmartField.h b/src/SmartField.h index d0405410..6da062ba 100644 --- a/src/SmartField.h +++ b/src/SmartField.h @@ -274,6 +274,15 @@ class SmartField { // Private implementation details - see https://herbsutter.com/gotw/_100/ class impl; std::unique_ptr pimpl; + + //! No copy constructor, as never want anyone, not even our friends, to make copies of a field object + SmartField(SmartField const&) = delete; + //! No assignment operator , as never want anyone, not even our friends, to make copies of a field object + SmartField& operator=(SmartField const&) = delete; + //! No move constructor + SmartField(SmartField &&) = delete; + //! No move assignment + SmartField & operator=(SmartField &&) = delete; }; #endif diff --git a/src/measurement/Unit.cpp b/src/measurement/Unit.cpp index c77c5c8c..29a5e50f 100644 --- a/src/measurement/Unit.cpp +++ b/src/measurement/Unit.cpp @@ -474,19 +474,19 @@ namespace Measurement::Units { // Per https://en.wikipedia.org/wiki/Beer_measurement, Plato and Brix are "essentially ... the same ([both based on // mass fraction of sucrose) [and only] differ in their conversion from weight percentage to specific gravity in the // fifth and sixth decimal places" - Unit const sp_grav {Measurement::UnitSystems::density_SpecificGravity, QObject::tr("sg"), [](double x){return x;}, [](double y){return y;}, 1.0}; + Unit const specificGravity {Measurement::UnitSystems::density_SpecificGravity, QObject::tr("sg"), [](double x){return x;}, [](double y){return y;}, 1.0}; Unit const plato {Measurement::UnitSystems::density_Plato, QObject::tr("P"), [](double x){return x == 0.0 ? 0.0 : Algorithms::PlatoToSG_20C20C(x);}, [](double y){return y == 0.0 ? 0.0 : Algorithms::SG_20C20C_toPlato(y);}, 1.0, - &sp_grav}; + &specificGravity}; Unit const brix {Measurement::UnitSystems::density_Brix, QObject::tr("brix"), [](double x){return x == 0.0 ? 0.0 : Algorithms::BrixToSgAt20C(x);}, [](double y){return y == 0.0 ? 0.0 : Algorithms::SgAt20CToBrix(y);}, 1.0, - &sp_grav}; + &specificGravity}; // == Diastatic power == Unit const lintner {Measurement::UnitSystems::diastaticPower_Lintner, QObject::tr("L"), [](double x){return x;}, [](double y){return y;}, 1.0}; Unit const wk {Measurement::UnitSystems::diastaticPower_WindischKolbach, QObject::tr("WK"), [](double x){return (x + 16) / 3.5;}, [](double y){return 3.5 * y - 16;}, 1.0, &lintner}; diff --git a/src/measurement/Unit.h b/src/measurement/Unit.h index e8171fd0..b912cf36 100644 --- a/src/measurement/Unit.h +++ b/src/measurement/Unit.h @@ -250,7 +250,7 @@ namespace Measurement { // usually interested in density in order to get % sugar content to do calculations about how much sugar turned to // alcohol. However, since our primary measurement (specific gravity) is of density, that's the physical quantity // under which we'll group all three units. - extern Unit const sp_grav; + extern Unit const specificGravity; extern Unit const plato; extern Unit const brix; // == Diastatic power == diff --git a/src/measurement/UnitSystem.cpp b/src/measurement/UnitSystem.cpp index 19096293..a9d3f2ca 100644 --- a/src/measurement/UnitSystem.cpp +++ b/src/measurement/UnitSystem.cpp @@ -458,7 +458,7 @@ namespace Measurement::UnitSystems { Measurement::SystemOfMeasurement::Lovibond}; UnitSystem const density_SpecificGravity{PhysicalQuantity::Density, - &Measurement::Units::sp_grav, + &Measurement::Units::specificGravity, "density_SpecificGravity", Measurement::SystemOfMeasurement::SpecificGravity}; diff --git a/src/model/NamedEntity.cpp b/src/model/NamedEntity.cpp index e11c8cae..c57a4d77 100644 --- a/src/model/NamedEntity.cpp +++ b/src/model/NamedEntity.cpp @@ -103,7 +103,7 @@ TypeLookup const NamedEntity::typeLookup { // everything else out. The only exception is that, for enums, we have to pretend they are stored as int, because // that's what's going to come out of the Qt property system (and it would significantly complicate other bits of // the code to separately register every different enum that we use.) - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::NamedEntity::deleted , NamedEntity::m_display ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::NamedEntity::deleted , NamedEntity::m_deleted ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::NamedEntity::display , NamedEntity::m_display ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::NamedEntity::folder , NamedEntity::m_folder ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::NamedEntity::key , NamedEntity::m_key ), diff --git a/src/model/Recipe.cpp b/src/model/Recipe.cpp index a52098cc..6a4b0890 100644 --- a/src/model/Recipe.cpp +++ b/src/model/Recipe.cpp @@ -1171,7 +1171,7 @@ void Recipe::generateInstructions() { addPreinstructions(miscSteps(Misc::Use::Primary)); str = tr("Let ferment until FG is %1.").arg( - Measurement::displayAmount(Measurement::Amount{fg(), Measurement::Units::sp_grav}, 3) + Measurement::displayAmount(Measurement::Amount{fg(), Measurement::Units::specificGravity}, 3) ); auto fermentIns = std::make_shared(); diff --git a/src/unitTests/Testing.cpp b/src/unitTests/Testing.cpp index 24617cab..954acd9d 100644 --- a/src/unitTests/Testing.cpp +++ b/src/unitTests/Testing.cpp @@ -633,7 +633,7 @@ void Testing::testUnitConversions() { testInput.clear(); testInputAsStream << "9" << decimalSeparator << "994 P"; QVERIFY2(fuzzyComp(Measurement::UnitSystems::density_Plato.qstringToSI(testInput, // "9.994 P" - Measurement::Units::sp_grav).quantity(), + Measurement::Units::specificGravity).quantity(), 1.040, 0.001), "Unit conversion error (Plato to SG)"); diff --git a/src/widgets/SmartLabel.cpp b/src/widgets/SmartLabel.cpp index 9a03dd4d..6b20cadd 100644 --- a/src/widgets/SmartLabel.cpp +++ b/src/widgets/SmartLabel.cpp @@ -24,6 +24,7 @@ #include #include +#include "Logging.h" #include "measurement/Measurement.h" #include "model/Style.h" #include "model/Recipe.h" @@ -39,9 +40,9 @@ class SmartLabel::impl { QWidget * parent) : m_self {self }, m_initialised {false}, - m_editorName {"Uninitialised m_editorName!" }, - m_labelName {"Uninitialised m_labelName!" }, - m_labelFqName {"Uninitialised m_labellFqName!"}, + m_editorName {"Uninitialised m_editorName!" }, + m_labelName {"Uninitialised m_labelName!" }, + m_labelFqName {"Uninitialised m_labelFqName!"}, m_typeInfo {nullptr}, m_parent {parent }, m_contextMenu {nullptr} { @@ -84,7 +85,6 @@ class SmartLabel::impl { return; } - SmartLabel & m_self ; bool m_initialised; char const * m_editorName ; @@ -109,6 +109,8 @@ void SmartLabel::init(char const * const editorName, char const * const labelFqName, SmartField * smartField, TypeInfo const & typeInfo) { + qDebug() << Q_FUNC_INFO << labelFqName << ":" << typeInfo; + this->pimpl->m_editorName = editorName ; this->pimpl->m_labelName = labelName ; this->pimpl->m_labelFqName = labelFqName; @@ -121,6 +123,10 @@ void SmartLabel::init(char const * const editorName, return; } +[[nodiscard]] bool SmartLabel::isInitialised() const { + return this->pimpl->m_initialised; +} + ///SmartField & SmartLabel::getBuddy() const { /// Q_ASSERT(this->pimpl->m_initialised); /// @@ -158,6 +164,7 @@ std::optional SmartLabel::getForcedRelat } SmartAmounts::ScaleInfo SmartLabel::getScaleInfo() const { + Q_ASSERT(this->pimpl->m_initialised); Q_ASSERT(!std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)); return SmartAmounts::getScaleInfo(this->pimpl->m_editorName, this->pimpl->m_labelName, @@ -165,6 +172,7 @@ SmartAmounts::ScaleInfo SmartLabel::getScaleInfo() const { } Measurement::UnitSystem const & SmartLabel::getDisplayUnitSystem() const { + Q_ASSERT(this->pimpl->m_initialised); // It's a coding error to call this for NonPhysicalQuantity, and we assert we never have a Mixed2PhysicalQuantities // for a SmartLabel that has no associated SmartField. Q_ASSERT(std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)); @@ -174,6 +182,7 @@ Measurement::UnitSystem const & SmartLabel::getDisplayUnitSystem() const { } double SmartLabel::getAmountToDisplay(double const canonicalValue) const { + Q_ASSERT(this->pimpl->m_initialised); // It's a coding error to call this for NonPhysicalQuantity, and we assert we never have a Mixed2PhysicalQuantities // for a SmartLabel that has no associated SmartField. Q_ASSERT(std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)); @@ -190,6 +199,15 @@ double SmartLabel::getAmountToDisplay(double const canonicalValue) const { QPair SmartLabel::getRangeToDisplay(double const canonicalValueMin, double const canonicalValueMax) const { + // Only need next bit for debugging! +// if (!this->pimpl->m_initialised) { +// qCritical() << Q_FUNC_INFO << this; +// qCritical().noquote() << Q_FUNC_INFO << this->pimpl->m_labelFqName << "Stack trace:" << Logging::getStackTrace(); +// } + Q_ASSERT(this->pimpl->m_initialised); + // It's a coding error to call this for NonPhysicalQuantity, and we assert we never have a Mixed2PhysicalQuantities + // for a SmartLabel that has no associated SmartField. + Q_ASSERT(std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)); auto const & canonicalUnit{ Measurement::Unit::getCanonicalUnit(std::get(*this->pimpl->m_typeInfo->fieldType)) }; @@ -198,7 +216,7 @@ QPair SmartLabel::getRangeToDisplay(double const canonicalValueMi return QPair( Measurement::amountDisplay(Measurement::Amount{canonicalValueMin, canonicalUnit}, forcedSystemOfMeasurement, forcedRelativeScale), Measurement::amountDisplay(Measurement::Amount{canonicalValueMax, canonicalUnit}, forcedSystemOfMeasurement, forcedRelativeScale) - ); + ); } void SmartLabel::enterEvent([[maybe_unused]] QEvent * event) { diff --git a/src/widgets/SmartLabel.h b/src/widgets/SmartLabel.h index 42f367df..a3211c5b 100644 --- a/src/widgets/SmartLabel.h +++ b/src/widgets/SmartLabel.h @@ -116,6 +116,16 @@ class SmartLabel : public QLabel { * Not sure how to signal the parent to redisplay */ SmartLabel(QWidget * parent); + + /** + * \brief We assert that one \c SmartLabel cannot be the parent of another. At time of writing, on GCC on Linux, + * if you have a function that takes, eg \c SmartLabel& but you accidentally pass it \c SmartLabel*, the + * compiler will try to construct a temporary \c SmartLabel with the \c SmartLabel* as a parameter. We'd much + * prefer a compiler error in this case, so we explicitly delete the constructor that the compiler is being + * overly-helpful in trying to call. + */ + SmartLabel(SmartLabel * parent) = delete; + virtual ~SmartLabel(); /** @@ -135,6 +145,11 @@ class SmartLabel : public QLabel { SmartField * smartField, TypeInfo const & typeInfo); + /** + * \return \c true if \c init or \c initFixed has been called, \c false otherwise + */ + [[nodiscard]] bool isInitialised() const; + void setForcedSystemOfMeasurement(std::optional systemOfMeasurement); void setForcedRelativeScale(std::optional relativeScale); std::optional getForcedSystemOfMeasurement() const; @@ -235,6 +250,14 @@ public slots: class impl; std::unique_ptr pimpl; + //! No copy constructor, as never want anyone, not even our friends, to make copies of a label object + SmartLabel(SmartLabel const&) = delete; + //! No assignment operator , as never want anyone, not even our friends, to make copies of a label object + SmartLabel& operator=(SmartLabel const&) = delete; + //! No move constructor + SmartLabel(SmartLabel &&) = delete; + //! No move assignment + SmartLabel & operator=(SmartLabel &&) = delete; }; #endif diff --git a/ui/mainWindow.ui b/ui/mainWindow.ui index fb81a35b..2f07245b 100644 --- a/ui/mainWindow.ui +++ b/ui/mainWindow.ui @@ -291,7 +291,7 @@ - + 0 @@ -307,7 +307,7 @@ - + 0 @@ -408,7 +408,7 @@ - + 0 @@ -657,7 +657,7 @@ - + 0 @@ -937,7 +937,7 @@ - + Boil Size @@ -2344,70 +2344,4 @@ - - - label_targetBoilSize - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_boilSize - lineChanged(PreviousScaleInfo) - - - 466 - 309 - - - 572 - 309 - - - - - label_boilSg - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_boilSg - lineChanged(PreviousScaleInfo) - - - 466 - 403 - - - 572 - 403 - - - - - label_boiltime - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_boilTime - lineChanged(PreviousScaleInfo) - - - 466 - 373 - - - 572 - 373 - - - - - label_targetBatchSize - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_batchSize - lineChanged(PreviousScaleInfo) - - - 466 - 277 - - - 572 - 277 - - - - diff --git a/ui/waterEditor.ui b/ui/waterEditor.ui index 9a91eb4b..117b3073 100644 --- a/ui/waterEditor.ui +++ b/ui/waterEditor.ui @@ -70,7 +70,7 @@ - + 0 @@ -108,7 +108,7 @@ - + 0 @@ -146,7 +146,7 @@ - + 0 @@ -184,7 +184,7 @@ - + 0 @@ -225,7 +225,7 @@ - + 0 @@ -263,7 +263,7 @@ - + 0 @@ -304,7 +304,7 @@ - + 0 @@ -424,6 +424,14 @@
+ + SmartLabel + QLabel +
widgets/SmartLabel.h
+ + changedSystemOfMeasurementOrScale(PreviousScaleInfo) + +
SmartLineEdit QLineEdit From 860d5758045e1f66d642de7856ec4a4cf7c2fe61 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Thu, 27 Apr 2023 09:27:49 +0200 Subject: [PATCH 21/42] Mostly tidy-up --- src/FermentableSortFilterProxyModel.cpp | 8 + src/HopDialog.h | 3 +- src/HopSortFilterProxyModel.cpp | 6 + src/HopSortFilterProxyModel.h | 9 +- src/MiscEditor.h | 2 +- src/RadarChart.cpp | 10 ++ src/SmartField.cpp | 4 +- src/measurement/Measurement.cpp | 195 --------------------- src/measurement/Measurement.h | 108 ------------ src/widgets/SmartLineEdit.cpp | 16 -- translations/bt_ca.ts | 136 --------------- translations/bt_cs.ts | 136 --------------- translations/bt_de.ts | 136 --------------- translations/bt_el.ts | 136 --------------- translations/bt_en.ts | 136 --------------- translations/bt_es.ts | 136 --------------- translations/bt_et.ts | 136 --------------- translations/bt_eu.ts | 136 --------------- translations/bt_fr.ts | 136 --------------- translations/bt_gl.ts | 136 --------------- translations/bt_hu.ts | 136 --------------- translations/bt_it.ts | 136 --------------- translations/bt_lv.ts | 136 --------------- translations/bt_nb.ts | 136 --------------- translations/bt_nl.ts | 136 --------------- translations/bt_pl.ts | 136 --------------- translations/bt_pt.ts | 136 --------------- translations/bt_ru.ts | 136 --------------- translations/bt_sr.ts | 136 --------------- translations/bt_sv.ts | 136 --------------- translations/bt_tr.ts | 136 --------------- translations/bt_zh.ts | 136 --------------- ui/brewNoteWidget.ui | 198 --------------------- ui/equipmentEditor.ui | 223 ------------------------ ui/fermentableEditor.ui | 102 ----------- ui/hopEditor.ui | 60 ------- ui/mainWindow.ui | 24 --- ui/mashDesigner.ui | 40 ----- ui/mashEditor.ui | 82 --------- ui/mashStepEditor.ui | 133 -------------- ui/miscEditor.ui | 6 - ui/namedMashEditor.ui | 66 ------- ui/ogAdjuster.ui | 100 ----------- ui/pitchDialog.ui | 81 --------- ui/primingDialog.ui | 59 ------- ui/strikeWaterDialog.ui | 33 ---- ui/styleEditor.ui | 158 ----------------- ui/waterDialog.ui | 135 -------------- ui/yeastEditor.ui | 43 ----- 49 files changed, 34 insertions(+), 4862 deletions(-) diff --git a/src/FermentableSortFilterProxyModel.cpp b/src/FermentableSortFilterProxyModel.cpp index 91b141a8..aa569b2b 100644 --- a/src/FermentableSortFilterProxyModel.cpp +++ b/src/FermentableSortFilterProxyModel.cpp @@ -89,6 +89,14 @@ bool FermentableSortFilterProxyModel::lessThan(QModelIndex const & left, } return leftAmount < rightAmount; } + + case FermentableTableModel::ColumnIndex::Name : + case FermentableTableModel::ColumnIndex::Type : + case FermentableTableModel::ColumnIndex::IsWeight : + case FermentableTableModel::ColumnIndex::IsMashed : + case FermentableTableModel::ColumnIndex::AfterBoil: + // Nothing to do for these cases + break; } return leftFermentable.toString() < rightFermentable.toString(); diff --git a/src/HopDialog.h b/src/HopDialog.h index 2280c9ec..35484a39 100644 --- a/src/HopDialog.h +++ b/src/HopDialog.h @@ -1,7 +1,8 @@ /*====================================================================================================================== - * HopDialog.h is part of Brewken, and is copyright the following authors 2009-2015: + * HopDialog.h is part of Brewken, and is copyright the following authors 2009-2023: * • Daniel Pettersson * • Jeff Bailey + * • Matt Young * • Mik Firestone * • Philip Greggory Lee * diff --git a/src/HopSortFilterProxyModel.cpp b/src/HopSortFilterProxyModel.cpp index 9bebb749..0b25e5f8 100644 --- a/src/HopSortFilterProxyModel.cpp +++ b/src/HopSortFilterProxyModel.cpp @@ -80,6 +80,12 @@ bool HopSortFilterProxyModel::lessThan(QModelIndex const & left, return lUse < rUse; } + + case HopTableModel::ColumnIndex::Name: + case HopTableModel::ColumnIndex::Form: + case HopTableModel::ColumnIndex::Use : + // Nothing to do for these cases + break; } return leftHop.toString() < rightHop.toString(); diff --git a/src/HopSortFilterProxyModel.h b/src/HopSortFilterProxyModel.h index 5bf78eb4..1c50272d 100644 --- a/src/HopSortFilterProxyModel.h +++ b/src/HopSortFilterProxyModel.h @@ -1,5 +1,6 @@ /*====================================================================================================================== - * HopSortFilterProxyModel.h is part of Brewken, and is copyright the following authors 2009-2014: + * HopSortFilterProxyModel.h is part of Brewken, and is copyright the following authors 2009-2023: + * • Matt Young * • Mik Firestone * • Philip Greggory Lee * @@ -29,11 +30,11 @@ class HopSortFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: - HopSortFilterProxyModel(QObject *parent = 0, bool filt = true); + HopSortFilterProxyModel(QObject * parent = 0, bool filt = true); protected: - bool lessThan(const QModelIndex &left, const QModelIndex &right) const; - bool filterAcceptsRow( int source_row, const QModelIndex &source_parent) const; + bool lessThan(QModelIndex const & left, QModelIndex const & right) const; + bool filterAcceptsRow(int source_row, QModelIndex const & source_parent) const; private: bool filter; diff --git a/src/MiscEditor.h b/src/MiscEditor.h index 0bef9135..f6b7ea65 100644 --- a/src/MiscEditor.h +++ b/src/MiscEditor.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * MiscEditor.h is part of Brewken, and is copyright the following authors 2009-2021: + * MiscEditor.h is part of Brewken, and is copyright the following authors 2009-2023: * • Jeff Bailey * • Matt Young * • Mik Firestone diff --git a/src/RadarChart.cpp b/src/RadarChart.cpp index c1e1f5d5..47299d22 100644 --- a/src/RadarChart.cpp +++ b/src/RadarChart.cpp @@ -17,7 +17,17 @@ #include #include + +// +// C++20 introduces std::numbers::pi, which has better cross-platform support than the M_PI constant in cmath. However, +// older versions of GCC (eg as shipped with Ubuntu 20.04 LTS) do not ship with the new header. +// +#if defined(__GNUC__) && (__GNUC__ < 10) +// I hope this is allowed. We'll be able to get rid of it once we stop needing to support old versions of GCC +constexpr double std::numbers::pi = M_PI; +#else #include // For std::numbers::pi +#endif #include #include diff --git a/src/SmartField.cpp b/src/SmartField.cpp index 286dea6f..85a976e4 100644 --- a/src/SmartField.cpp +++ b/src/SmartField.cpp @@ -232,7 +232,7 @@ template<> void SmartField::init(char const * const template<> void SmartField::init(char const * const editorName, char const * const fieldName, char const * const fieldFqName, - QLabel & regularBuddyLabel, + [[maybe_unused]] QLabel & regularBuddyLabel, TypeInfo const & typeInfo, std::optional const precision, QString const & maximalDisplayString) { @@ -254,7 +254,7 @@ template<> void SmartField::init(char const * const edi void SmartField::initFixed(char const * const editorName, char const * const fieldName, char const * const fieldFqName, - QLabel & buddyLabel, + [[maybe_unused]] QLabel & buddyLabel, TypeInfo const & typeInfo, Measurement::Unit const & fixedDisplayUnit, std::optional const precision, diff --git a/src/measurement/Measurement.cpp b/src/measurement/Measurement.cpp index a5064711..44eb3ca0 100644 --- a/src/measurement/Measurement.cpp +++ b/src/measurement/Measurement.cpp @@ -193,47 +193,9 @@ QString Measurement::displayAmount(Measurement::Amount const & amount, forcedSystemOfMeasurement ? UnitSystem::getInstance(*forcedSystemOfMeasurement, physicalQuantity) : Measurement::getDisplayUnitSystem(physicalQuantity); -// qDebug() << Q_FUNC_INFO << "Display" << amount << units->getUnitName() << "in" << temp->unitType(); return displayUnitSystem.displayAmount(amount, precision, forcedScale); } -///QString Measurement::displayAmount(NamedEntity * namedEntity, -/// QObject * guiObject, -/// BtStringConst const & propertyName, -/// Measurement::Unit const & units, -/// int precision ) { -/// -/// if (namedEntity->property(*propertyName).canConvert(QVariant::Double)) { -/// // Get the amount -/// QString value = namedEntity->property(*propertyName).toString(); -/// bool ok = false; -/// double quantity = Localization::toDouble(value, &ok); -/// if (!ok) { -/// qWarning() << Q_FUNC_INFO << "Could not convert " << value << " to double"; -/// } -/// -/// return Measurement::displayAmount( -/// Measurement::Amount{quantity, units}, -/// precision, -/// Measurement::getForcedSystemOfMeasurementForField(*propertyName, guiObject->objectName()), -/// Measurement::getForcedRelativeScaleForField(*propertyName, guiObject->objectName()) -/// ); -/// } -/// -/// return "?"; -///} - -///QString Measurement::displayAmount(Measurement::Amount const & amount, -/// BtStringConst const & section, -/// BtStringConst const & propertyName, -/// int precision) { -/// return Measurement::displayAmount(amount, -/// precision, -/// Measurement::getForcedSystemOfMeasurementForField(*propertyName, *section), -/// Measurement::getForcedRelativeScaleForField(*propertyName, *section)); -/// -///} - double Measurement::amountDisplay(Measurement::Amount const & amount, std::optional forcedSystemOfMeasurement, std::optional forcedScale) { @@ -253,68 +215,6 @@ double Measurement::amountDisplay(Measurement::Amount const & amount, return displayUnitSystem.amountDisplay(amount, forcedScale); } -///double Measurement::amountDisplay(NamedEntity * namedEntity, -/// QObject * guiObject, -/// BtStringConst const & propertyName, -/// Measurement::Unit const * units) { -/// -/// if (namedEntity->property(*propertyName).canConvert(QVariant::Double)) { -/// // Get the amount -/// QString value = namedEntity->property(*propertyName).toString(); -/// bool ok = false; -/// double amount = Localization::toDouble(value, &ok); -/// if (!ok) { -/// qWarning() << Q_FUNC_INFO << "Could not convert" << value << "to double"; -/// } -/// -/// // Special case: we don't know the units of the supplied amount, so just return it as is -/// if (units == nullptr) { -/// return amount; -/// } -/// -/// return Measurement::amountDisplay( -/// Measurement::Amount{amount, *units}, -/// Measurement::getForcedSystemOfMeasurementForField(*propertyName, guiObject->objectName()), -/// Measurement::getForcedRelativeScaleForField(*propertyName, guiObject->objectName()) -/// ); -/// } -/// -/// return -1.0; -///} - -///QPair Measurement::displayRange(NamedEntity* namedEntity, -/// QObject *guiObject, -/// BtStringConst const & propertyNameMin, -/// BtStringConst const & propertyNameMax, -/// Unit const * units) { -/// QPair range; -/// -/// if (!namedEntity) { -/// range.first = 0.0; -/// range.second = 100.0; -/// } else { -/// range.first = amountDisplay(namedEntity, guiObject, propertyNameMin, units); -/// range.second = amountDisplay(namedEntity, guiObject, propertyNameMax, units); -/// } -/// -/// return range; -///} -/// -///QPair Measurement::displayRange(QObject *guiObject, -/// BtStringConst const & propertyName, -/// double min, -/// double max, -/// Unit const & units) { -/// auto forcedSystemOfMeasurement = Measurement::getForcedSystemOfMeasurementForField(*propertyName, -/// guiObject->objectName()); -/// auto forcedRelativeScale = Measurement::getForcedRelativeScaleForField(*propertyName, guiObject->objectName()); -/// -/// QPair range; -/// range.first = Measurement::amountDisplay(Measurement::Amount{min, units}, forcedSystemOfMeasurement, forcedRelativeScale); -/// range.second = Measurement::amountDisplay(Measurement::Amount{max, units}, forcedSystemOfMeasurement, forcedRelativeScale); -/// return range; -///} - void Measurement::getThicknessUnits(Unit const ** volumeUnit, Unit const ** weightUnit) { *volumeUnit = Measurement::getDisplayUnitSystem(Measurement::PhysicalQuantity::Volume).thicknessUnit(); *weightUnit = Measurement::getDisplayUnitSystem(Measurement::PhysicalQuantity::Mass).thicknessUnit(); @@ -370,98 +270,3 @@ Measurement::Amount Measurement::qStringToSI(QString qstr, return displayUnitSystem.qstringToSI(qstr, *defaultUnit); } - -///std::optional Measurement::getForcedSystemOfMeasurementForField(QString const & field, -/// QString const & section) { -/// if (field.isEmpty()) { -/// return std::nullopt; -/// } -/// return Measurement::getFromUniqueName( -/// PersistentSettings::value(field, -/// "None", // This, or any invalid name, will give "no value" return from getFromUniqueName() -/// section, -/// PersistentSettings::Extension::UNIT).toString() -/// ); -///} -/// -///std::optional Measurement::getForcedRelativeScaleForField(QString const & field, -/// QString const & section) { -/// if (field.isEmpty()) { -/// return std::nullopt; -/// } -/// return Measurement::UnitSystem::getScaleFromUniqueName( -/// PersistentSettings::value(field, -/// "None", // This, or any invalid name, will give "no value" return from getFromUniqueName() -/// section, -/// PersistentSettings::Extension::SCALE).toString() -/// ); -///} - -///void Measurement::setForcedSystemOfMeasurementForField(QString const & field, -/// QString const & section, -/// std::optional forcedSystemOfMeasurement) { -/// if (field.isEmpty()) { -/// return; -/// } -/// if (forcedSystemOfMeasurement) { -/// PersistentSettings::insert(field, -/// Measurement::getUniqueName(*forcedSystemOfMeasurement), -/// section, -/// PersistentSettings::Extension::UNIT); -/// } else { -/// PersistentSettings::remove(field, -/// section, -/// PersistentSettings::Extension::UNIT); -/// } -/// return; -///} -/// -///void Measurement::setForcedRelativeScaleForField(QString const & field, -/// QString const & section, -/// std::optional forcedScale) { -/// if (field.isEmpty()) { -/// return; -/// } -/// if (forcedScale) { -/// PersistentSettings::insert(field, -/// Measurement::UnitSystem::getUniqueName(*forcedScale), -/// section, -/// PersistentSettings::Extension::SCALE); -/// } else { -/// PersistentSettings::remove(field, -/// section, -/// PersistentSettings::Extension::SCALE); -/// } -/// return; -///} - -///Measurement::SystemOfMeasurement Measurement::getSystemOfMeasurementForField(QString const & field, -/// QString const & section, -/// Measurement::PhysicalQuantities physicalQuantities) { -/// auto forcedSystemOfMeasurement = Measurement::getForcedSystemOfMeasurementForField(field, section); -/// if (forcedSystemOfMeasurement) { -/// return *forcedSystemOfMeasurement; -/// } -/// -/// // If there is no forced System Of Measurement for the field, then we can look to the globally-set UnitSystem for -/// // this PhysicalQuantity -- except that, if there are two values of PhysicalQuantity, we have to -/// // choose one arbitrarily. The end result should be the same, because Mass & Volume share the same -/// // SystemOfMeasurement, as do MassConcentration & VolumeConcentration. -/// Measurement::PhysicalQuantity const physicalQuantity = -/// std::holds_alternative(physicalQuantities) ? -/// std::get(physicalQuantities) : -/// std::get<0>(std::get(physicalQuantities)); -/// -/// return Measurement::getDisplayUnitSystem(physicalQuantity).systemOfMeasurement; -///} -/// -/// -///Measurement::UnitSystem const & Measurement::getUnitSystemForField(QString const & field, -/// QString const & section, -/// Measurement::PhysicalQuantity physicalQuantity) { -/// auto forcedSystemOfMeasurement = Measurement::getForcedSystemOfMeasurementForField(field, section); -/// if (forcedSystemOfMeasurement) { -/// return Measurement::UnitSystem::getInstance(*forcedSystemOfMeasurement, physicalQuantity); -/// } -/// return Measurement::getDisplayUnitSystem(physicalQuantity); -///} diff --git a/src/measurement/Measurement.h b/src/measurement/Measurement.h index fcb3000d..8386efe1 100644 --- a/src/measurement/Measurement.h +++ b/src/measurement/Measurement.h @@ -81,37 +81,6 @@ namespace Measurement { std::optional forcedSystemOfMeasurement = std::nullopt, std::optional forcedScale = std::nullopt); -/// /*! -/// * \brief Converts a measurement (aka amount) to a displayable string in the appropriate units. -/// * -/// * \param namedEntity Named Entity of which we want to display a property -/// * \param guiObject the GUI object doing the display, used to access configured unit system & scale -/// * \param propertyName the \c QObject::property of \c namedEntity that returns the amount we wish to display -/// * \param units which unit system it is in -/// * \param precision how many decimal places to use, defaulting to 3 -/// */ -/// QString displayAmount(NamedEntity * namedEntity, -/// QObject * guiObject, -/// BtStringConst const & propertyName, -/// Measurement::Unit const & units, -/// int precision = 3); - -/// /*! -/// * \brief Converts a measurement (aka amount) to a displayable string in the appropriate units. -/// * -/// * .:TBD:. Need to check we do the right thing for measurements that can be either mass or volume (eg Misc, -/// * Fermentable, etc) -/// * -/// * \param amount the amount to display -/// * \param section the name of the object to reference to get unit system & scales from the config file -/// * \param propertyName the property name to complete the lookup for units & scales -/// * \param precision how many decimal places to use, defaulting to 3 -/// */ -/// QString displayAmount(Measurement::Amount const & amount, -/// BtStringConst const & section, -/// BtStringConst const & propertyName, -/// int precision = 3); - /*! * \brief Converts a measurement (aka amount) to its numerical equivalent in the specified or default units. * @@ -124,51 +93,6 @@ namespace Measurement { std::optional forcedSystemOfMeasurement = std::nullopt, std::optional forcedScale = std::nullopt); -/// /*! -/// * \brief Converts a measurement (aka amount) to its numerical equivalent in the specified or default units. -/// * -/// * \param namedEntity Named Entity of which we want to display a property -/// * \param guiObject the GUI object doing the display, used to access configured unit system & scale -/// * \param propertyName the \c QObject::property of \c namedEntity that returns the amount we wish to display -/// * \param units the units that the measurement (amount) is in -/// */ -/// double amountDisplay(NamedEntity* namedEntity, -/// QObject* guiObject, -/// BtStringConst const & propertyName, -/// Unit const * units = nullptr); -/// -/// /** -/// * \brief Converts a range (ie min/max pair) of measurements (aka amounts) to its numerical equivalent in whatever -/// * units are configured for this property. -/// * -/// * \param namedEntity Named Entity of which we want to display a property -/// * \param guiObject the GUI object doing the display, used to access configured unit system & scale -/// * \param propertyNameMin -/// * \param propertyNameMax -/// * \param units the units that the measurement (amount) is in -/// */ -/// QPair displayRange(NamedEntity* namedEntity, -/// QObject *guiObject, -/// BtStringConst const & propertyNameMin, -/// BtStringConst const & propertyNameMax, -/// Unit const * units = nullptr); -/// -/// /** -/// * \brief Converts a range (ie min/max pair) of measurements (aka amounts) to its numerical equivalent in whatever -/// * units are configured for this property. -/// * -/// * \param guiObject the GUI object doing the display, used to access configured unit system & scale -/// * \param propertyName -/// * \param min -/// * \param max -/// * \param units the units that the measurement (amount) is in -/// */ -/// QPair displayRange(QObject *guiObject, -/// BtStringConst const & propertyName, -/// double min, -/// double max, -/// Unit const & units); - //! \brief Displays thickness in appropriate units from standard thickness in L/kg. QString displayThickness(double thick_lkg, bool showUnits = true); //! \brief Appropriate thickness units will be placed in \c *volumeUnit and \c *weightUnit. @@ -190,38 +114,6 @@ namespace Measurement { Measurement::PhysicalQuantity const physicalQuantity, std::optional forcedSystemOfMeasurement = std::nullopt, std::optional forcedScale = std::nullopt); - - -/// /** -/// * \brief .:TODO:. Need some additional thought on how \c getForcedSystemOfMeasurementForField and -/// * \c getForcedRelativeScaleForField should work for fields that can be either mass or volume. -/// */ -/// std::optional getForcedSystemOfMeasurementForField(QString const & field, -/// QString const & section); -/// std::optional getForcedRelativeScaleForField(QString const & field, -/// QString const & section); -/// -/// void setForcedSystemOfMeasurementForField(QString const & field, -/// QString const & section, -/// std::optional forcedSystemOfMeasurement); -/// -/// void setForcedRelativeScaleForField(QString const & field, -/// QString const & section, -/// std::optional forcedScale); -/// -/// /** -/// * \brief Returns the \c SystemOfMeasurement that should be used to display this field, based on the forced -/// * \c SystemOfMeasurement for the field if there is one or otherwise on the the system-wide default -/// * \c UnitSystem for the specified \c PhysicalQuantity. -/// */ -/// Measurement::SystemOfMeasurement getSystemOfMeasurementForField(Measurement::PhysicalQuantities physicalQuantities); -/// -/// /** -/// * \brief Returns the \c UnitSystem that should be used to display this field, based on the forced -/// * \c SystemOfMeasurement for the field if there is one or otherwise on the the system-wide default -/// * \c UnitSystem for the specified \c PhysicalQuantity. -/// */ -/// Measurement::UnitSystem const & getUnitSystemForField(Measurement::PhysicalQuantity physicalQuantity); } #endif diff --git a/src/widgets/SmartLineEdit.cpp b/src/widgets/SmartLineEdit.cpp index d5989ca2..0f51aaae 100644 --- a/src/widgets/SmartLineEdit.cpp +++ b/src/widgets/SmartLineEdit.cpp @@ -47,22 +47,6 @@ class SmartLineEdit::impl { m_desiredWidthInPixels{0} { return; } -/// impl(SmartLineEdit & self) : -/// m_self {self}, -/// m_initialised {false}, -/// m_editorName {"Uninitialised m_editorName!" }, -/// m_lineEditName {"Uninitialised m_lineEditName!" }, -/// m_lineEditlFqName {"Uninitialised m_lineEditlFqName!"}, -/// m_typeInfo {nullptr}, -/// m_fixedDisplayUnit {nullptr}, -/// m_smartBuddyLabel {nullptr}, -/// m_uiAmountWithUnits {nullptr}, -/// m_precision {3}, -/// m_maximalDisplayString{"100.000 srm"}, -/// m_desiredWidthInPixels{0} { -/// return; -/// } -/// ~impl() = default; void calculateDisplaySize(QString const & maximalDisplayString) { diff --git a/translations/bt_ca.ts b/translations/bt_ca.ts index 7f5c9053..17908797 100644 --- a/translations/bt_ca.ts +++ b/translations/bt_ca.ts @@ -5282,22 +5282,10 @@ El volum final al primari és de %1. Name Nom - - name - - Required - - boilSize_l - - - - batchSize_l - - Time Temps @@ -5314,38 +5302,18 @@ El volum final al primari és de %1. Evaporation rate (per hr) Tassa d'evaporació (per hora) - - evapRate_lHr - - Final top-up water Aigua agregada al final - - topUpWater_l - - Kettle top-up water Aigua agregada a l'olla - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -5362,10 +5330,6 @@ El volum final al primari és de %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water Punt d'ebullició @@ -5382,26 +5346,10 @@ El volum final al primari és de %1. Volume of mash tun Volum del macerador - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes Notes @@ -5581,10 +5529,6 @@ El volum final al primari és de %1. Required - - name - - Yield % Rendiment % @@ -5593,18 +5537,10 @@ El volum final al primari és de %1. Extras Extres - - origin - - Diastatic power - - supplier - - Notes Notes @@ -8014,26 +7950,6 @@ El volum final al primari és de %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -8139,34 +8055,14 @@ El volum final al primari és de %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -8183,14 +8079,6 @@ El volum final al primari és de %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -8203,50 +8091,26 @@ El volum final al primari és de %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_cs.ts b/translations/bt_cs.ts index feef921f..775bc4c6 100644 --- a/translations/bt_cs.ts +++ b/translations/bt_cs.ts @@ -5200,22 +5200,10 @@ Celkový objem pro hlavní kvašení je %1. Name Název - - name - - Required - - boilSize_l - - - - batchSize_l - - Time Čas @@ -5232,38 +5220,18 @@ Celkový objem pro hlavní kvašení je %1. Evaporation rate (per hr) Odpar (za hodinu) - - evapRate_lHr - - Final top-up water Přilití vody po chmelovaru - - topUpWater_l - - Kettle top-up water Přilití vody před chmelovarem - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -5280,10 +5248,6 @@ Celkový objem pro hlavní kvašení je %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water Bod varu vody @@ -5300,26 +5264,10 @@ Celkový objem pro hlavní kvašení je %1. Volume of mash tun Objem rmutovací pánve - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes Poznámky @@ -5501,10 +5449,6 @@ Celkový objem pro hlavní kvašení je %1. Required - - name - - Yield % Vytěžnost % @@ -5513,18 +5457,10 @@ Celkový objem pro hlavní kvašení je %1. Extras Podrobnosti - - origin - - Diastatic power - - supplier - - Notes Poznámky @@ -7918,26 +7854,6 @@ Celkový objem pro hlavní kvašení je %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -8043,34 +7959,14 @@ Celkový objem pro hlavní kvašení je %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -8087,14 +7983,6 @@ Celkový objem pro hlavní kvašení je %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -8107,50 +7995,26 @@ Celkový objem pro hlavní kvašení je %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_de.ts b/translations/bt_de.ts index fc4e3d7b..4fa9bd32 100644 --- a/translations/bt_de.ts +++ b/translations/bt_de.ts @@ -5234,22 +5234,10 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. Name Name - - name - - Required - - boilSize_l - - - - batchSize_l - - Time Zeit @@ -5266,38 +5254,18 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. Evaporation rate (per hr) Verdampfungsrate (pro Stunde) - - evapRate_lHr - - Final top-up water - - topUpWater_l - - Kettle top-up water - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -5314,10 +5282,6 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water Siedepunkt von Wasser @@ -5334,26 +5298,10 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. Volume of mash tun Volumen des Maischbottichs - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes @@ -5533,10 +5481,6 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. Required - - name - - Yield % Ausbeute % @@ -5545,18 +5489,10 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. Extras Extras - - origin - - Diastatic power - - supplier - - Notes @@ -7942,26 +7878,6 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -8067,34 +7983,14 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -8111,14 +8007,6 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -8131,50 +8019,26 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_el.ts b/translations/bt_el.ts index bfbd0bf1..01b8c500 100644 --- a/translations/bt_el.ts +++ b/translations/bt_el.ts @@ -5204,22 +5204,10 @@ The final volume in the primary is %1. Name - - name - - Required - - boilSize_l - - - - batchSize_l - - Time Χρόνος @@ -5236,38 +5224,18 @@ The final volume in the primary is %1. Evaporation rate (per hr) Ποσοστό εξάτμισης (ανα ώρα) - - evapRate_lHr - - Final top-up water τελική προσθήκη νερού - - topUpWater_l - - Kettle top-up water Συμπηρωματική ποσότητα νερού την μαρμίτα - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -5284,10 +5252,6 @@ The final volume in the primary is %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water Σημείο βρασμού του νερού @@ -5304,26 +5268,10 @@ The final volume in the primary is %1. Volume of mash tun Όγκος σκεύους σακχαροποίησης - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes Σημειώσεις @@ -5503,10 +5451,6 @@ The final volume in the primary is %1. Required - - name - - Yield % Απόδοση % @@ -5515,18 +5459,10 @@ The final volume in the primary is %1. Extras Πρόσθετα - - origin - - Diastatic power - - supplier - - Notes Σημειώσεις @@ -7920,26 +7856,6 @@ The final volume in the primary is %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -8045,34 +7961,14 @@ The final volume in the primary is %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -8089,14 +7985,6 @@ The final volume in the primary is %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -8109,50 +7997,26 @@ The final volume in the primary is %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_en.ts b/translations/bt_en.ts index b861db84..666cd420 100644 --- a/translations/bt_en.ts +++ b/translations/bt_en.ts @@ -4415,22 +4415,10 @@ The final volume in the primary is %1. Name - - name - - Required - - boilSize_l - - - - batchSize_l - - Time @@ -4447,38 +4435,18 @@ The final volume in the primary is %1. Evaporation rate (per hr) - - evapRate_lHr - - Final top-up water - - topUpWater_l - - Kettle top-up water - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -4495,10 +4463,6 @@ The final volume in the primary is %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water @@ -4515,26 +4479,10 @@ The final volume in the primary is %1. Volume of mash tun - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes @@ -4666,10 +4614,6 @@ The final volume in the primary is %1. Required - - name - - Yield % @@ -4678,18 +4622,10 @@ The final volume in the primary is %1. Extras - - origin - - Diastatic power - - supplier - - Notes @@ -6735,26 +6671,6 @@ The final volume in the primary is %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -6856,34 +6772,14 @@ The final volume in the primary is %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -6900,14 +6796,6 @@ The final volume in the primary is %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -6920,50 +6808,26 @@ The final volume in the primary is %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_es.ts b/translations/bt_es.ts index 2c675943..69e4ac0a 100644 --- a/translations/bt_es.ts +++ b/translations/bt_es.ts @@ -5266,22 +5266,10 @@ El volumen final en el primario es %1. Name Nombre - - name - - Required - - boilSize_l - - - - batchSize_l - - Time Tiempo @@ -5298,38 +5286,18 @@ El volumen final en el primario es %1. Evaporation rate (per hr) Tasa de evaporación (por hr) - - evapRate_lHr - - Final top-up water Agua agregada al fin - - topUpWater_l - - Kettle top-up water Agua agregada a la olla - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -5346,10 +5314,6 @@ El volumen final en el primario es %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water Punto de ebullición @@ -5366,26 +5330,10 @@ El volumen final en el primario es %1. Volume of mash tun Volumen de olla de macerado - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes Notas @@ -5565,10 +5513,6 @@ El volumen final en el primario es %1. Required - - name - - Yield % Potencial de extracto % @@ -5577,18 +5521,10 @@ El volumen final en el primario es %1. Extras Extras - - origin - - Diastatic power - - supplier - - Notes Notas @@ -7978,26 +7914,6 @@ El volumen final en el primario es %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -8103,34 +8019,14 @@ El volumen final en el primario es %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -8147,14 +8043,6 @@ El volumen final en el primario es %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -8167,50 +8055,26 @@ El volumen final en el primario es %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_et.ts b/translations/bt_et.ts index 997e0d2b..c222ebb1 100644 --- a/translations/bt_et.ts +++ b/translations/bt_et.ts @@ -4473,22 +4473,10 @@ The final volume in the primary is %1. Name - - name - - Required - - boilSize_l - - - - batchSize_l - - Time Kestus @@ -4505,38 +4493,18 @@ The final volume in the primary is %1. Evaporation rate (per hr) - - evapRate_lHr - - Final top-up water - - topUpWater_l - - Kettle top-up water - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -4553,10 +4521,6 @@ The final volume in the primary is %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water @@ -4573,26 +4537,10 @@ The final volume in the primary is %1. Volume of mash tun - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes @@ -4724,10 +4672,6 @@ The final volume in the primary is %1. Required - - name - - Yield % @@ -4736,18 +4680,10 @@ The final volume in the primary is %1. Extras - - origin - - Diastatic power - - supplier - - Notes @@ -6801,26 +6737,6 @@ The final volume in the primary is %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -6922,34 +6838,14 @@ The final volume in the primary is %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -6966,14 +6862,6 @@ The final volume in the primary is %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -6986,50 +6874,26 @@ The final volume in the primary is %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_eu.ts b/translations/bt_eu.ts index 47019e79..24dec40e 100644 --- a/translations/bt_eu.ts +++ b/translations/bt_eu.ts @@ -4485,22 +4485,10 @@ The final volume in the primary is %1. Name - - name - - Required - - boilSize_l - - - - batchSize_l - - Time Denbora @@ -4517,38 +4505,18 @@ The final volume in the primary is %1. Evaporation rate (per hr) - - evapRate_lHr - - Final top-up water - - topUpWater_l - - Kettle top-up water - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -4565,10 +4533,6 @@ The final volume in the primary is %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water @@ -4585,26 +4549,10 @@ The final volume in the primary is %1. Volume of mash tun - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes @@ -4736,10 +4684,6 @@ The final volume in the primary is %1. Required - - name - - Yield % @@ -4748,18 +4692,10 @@ The final volume in the primary is %1. Extras - - origin - - Diastatic power - - supplier - - Notes @@ -6813,26 +6749,6 @@ The final volume in the primary is %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -6934,34 +6850,14 @@ The final volume in the primary is %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -6978,14 +6874,6 @@ The final volume in the primary is %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -6998,50 +6886,26 @@ The final volume in the primary is %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_fr.ts b/translations/bt_fr.ts index 3c2288e8..23a72c80 100644 --- a/translations/bt_fr.ts +++ b/translations/bt_fr.ts @@ -5288,22 +5288,10 @@ Le volume final dans la cuve de fermentation est de %1. Name Nom - - name - - Required - - boilSize_l - - - - batchSize_l - - Time @@ -5320,38 +5308,18 @@ Le volume final dans la cuve de fermentation est de %1. Evaporation rate (per hr) Taux d'évaporation (par h) - - evapRate_lHr - - Final top-up water Ajout d'eau dans le fermenteur - - topUpWater_l - - Kettle top-up water Ajout d'eau avant l'ébullition - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -5368,10 +5336,6 @@ Le volume final dans la cuve de fermentation est de %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water Point d'ébullition de l'eau @@ -5388,26 +5352,10 @@ Le volume final dans la cuve de fermentation est de %1. Volume of mash tun Volume de la cuve matière - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes Notes @@ -5587,10 +5535,6 @@ Le volume final dans la cuve de fermentation est de %1. Required - - name - - Yield % Rendement % @@ -5599,18 +5543,10 @@ Le volume final dans la cuve de fermentation est de %1. Extras Extras - - origin - - Diastatic power - - supplier - - Notes Notes @@ -8020,26 +7956,6 @@ Le volume final dans la cuve de fermentation est de %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -8145,34 +8061,14 @@ Le volume final dans la cuve de fermentation est de %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -8189,14 +8085,6 @@ Le volume final dans la cuve de fermentation est de %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -8209,50 +8097,26 @@ Le volume final dans la cuve de fermentation est de %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_gl.ts b/translations/bt_gl.ts index 1daf809e..7fce4a71 100644 --- a/translations/bt_gl.ts +++ b/translations/bt_gl.ts @@ -4619,22 +4619,10 @@ The final volume in the primary is %1. Name Nome - - name - - Required - - boilSize_l - - - - batchSize_l - - Time Tempo @@ -4651,38 +4639,18 @@ The final volume in the primary is %1. Evaporation rate (per hr) - - evapRate_lHr - - Final top-up water - - topUpWater_l - - Kettle top-up water - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -4699,10 +4667,6 @@ The final volume in the primary is %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water @@ -4719,26 +4683,10 @@ The final volume in the primary is %1. Volume of mash tun - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes Anotacións @@ -4870,10 +4818,6 @@ The final volume in the primary is %1. Required - - name - - Yield % @@ -4882,18 +4826,10 @@ The final volume in the primary is %1. Extras - - origin - - Diastatic power - - supplier - - Notes Anotacións @@ -6975,26 +6911,6 @@ The final volume in the primary is %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -7096,34 +7012,14 @@ The final volume in the primary is %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -7140,14 +7036,6 @@ The final volume in the primary is %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -7160,50 +7048,26 @@ The final volume in the primary is %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_hu.ts b/translations/bt_hu.ts index 81540d42..c1b0ffbe 100644 --- a/translations/bt_hu.ts +++ b/translations/bt_hu.ts @@ -5230,22 +5230,10 @@ Végleges mennyiség az elsődleges erjesztőben: %1 Name Név - - name - - Required - - boilSize_l - - - - batchSize_l - - Time Idõ @@ -5262,38 +5250,18 @@ Végleges mennyiség az elsődleges erjesztőben: %1 Evaporation rate (per hr) Párolgási arány (óránként) - - evapRate_lHr - - Final top-up water A főzés utolsó fázisban hozzáadott víz mennyisége - - topUpWater_l - - Kettle top-up water Vízpótlás mennyisége forraláskor - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -5310,10 +5278,6 @@ Végleges mennyiség az elsődleges erjesztőben: %1 Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water Víz forráspontja @@ -5330,26 +5294,10 @@ Végleges mennyiség az elsődleges erjesztőben: %1 Volume of mash tun Cefréző edény térfogata - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes Megjegyzések @@ -5529,10 +5477,6 @@ Végleges mennyiség az elsődleges erjesztőben: %1 Required - - name - - Yield % Süllyedés % @@ -5541,18 +5485,10 @@ Végleges mennyiség az elsődleges erjesztőben: %1 Extras Extrák - - origin - - Diastatic power - - supplier - - Notes Megjegyzések @@ -7846,26 +7782,6 @@ Végleges mennyiség az elsődleges erjesztőben: %1 Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -7971,34 +7887,14 @@ Végleges mennyiség az elsődleges erjesztőben: %1 totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -8015,14 +7911,6 @@ Végleges mennyiség az elsődleges erjesztőben: %1 SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -8035,50 +7923,26 @@ Végleges mennyiség az elsődleges erjesztőben: %1 CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_it.ts b/translations/bt_it.ts index 469bd70c..65cd2515 100644 --- a/translations/bt_it.ts +++ b/translations/bt_it.ts @@ -5286,22 +5286,10 @@ Il Volume finale del primo è %1. Name Nome - - name - - Required - - boilSize_l - - - - batchSize_l - - Time Tempo @@ -5318,38 +5306,18 @@ Il Volume finale del primo è %1. Evaporation rate (per hr) Tasso di Evaporazione (per ora) - - evapRate_lHr - - Final top-up water Finale rabbocco acqua - - topUpWater_l - - Kettle top-up water Rabbocco Acqua bollitore - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -5366,10 +5334,6 @@ Il Volume finale del primo è %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water Punto di ebollizione dell'acqua @@ -5386,26 +5350,10 @@ Il Volume finale del primo è %1. Volume of mash tun Volume di mash tun - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes @@ -5585,10 +5533,6 @@ Il Volume finale del primo è %1. Required - - name - - Yield % Prodotto @@ -5597,18 +5541,10 @@ Il Volume finale del primo è %1. Extras Extra - - origin - - Diastatic power - - supplier - - Notes @@ -8020,26 +7956,6 @@ Il Volume finale del primo è %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -8145,34 +8061,14 @@ Il Volume finale del primo è %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -8189,14 +8085,6 @@ Il Volume finale del primo è %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -8209,50 +8097,26 @@ Il Volume finale del primo è %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_lv.ts b/translations/bt_lv.ts index 221dfe48..9401acb0 100644 --- a/translations/bt_lv.ts +++ b/translations/bt_lv.ts @@ -4544,22 +4544,10 @@ The final volume in the primary is %1. Name Nosaukums - - name - - Required - - boilSize_l - - - - batchSize_l - - Time Laiks @@ -4576,38 +4564,18 @@ The final volume in the primary is %1. Evaporation rate (per hr) - - evapRate_lHr - - Final top-up water - - topUpWater_l - - Kettle top-up water - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -4624,10 +4592,6 @@ The final volume in the primary is %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water @@ -4644,26 +4608,10 @@ The final volume in the primary is %1. Volume of mash tun - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes Piezīmes @@ -4803,10 +4751,6 @@ The final volume in the primary is %1. Required - - name - - Yield % @@ -4815,18 +4759,10 @@ The final volume in the primary is %1. Extras - - origin - - Diastatic power - - supplier - - Notes Piezīmes @@ -6912,26 +6848,6 @@ The final volume in the primary is %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -7033,34 +6949,14 @@ The final volume in the primary is %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -7077,14 +6973,6 @@ The final volume in the primary is %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -7097,50 +6985,26 @@ The final volume in the primary is %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_nb.ts b/translations/bt_nb.ts index b77d5fa2..bff5f635 100644 --- a/translations/bt_nb.ts +++ b/translations/bt_nb.ts @@ -5196,22 +5196,10 @@ Sluttvolumet i primærgjæringskaret er %1. Name Navn - - name - - Required - - boilSize_l - - - - batchSize_l - - Time @@ -5228,38 +5216,18 @@ Sluttvolumet i primærgjæringskaret er %1. Evaporation rate (per hr) Frodampningsrate (pr time) - - evapRate_lHr - - Final top-up water Endelig etterfyllingsvann - - topUpWater_l - - Kettle top-up water Etterfyllingsvann kjele - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -5276,10 +5244,6 @@ Sluttvolumet i primærgjæringskaret er %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water Kokepunkt for vann @@ -5296,26 +5260,10 @@ Sluttvolumet i primærgjæringskaret er %1. Volume of mash tun Volum på meskekar - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes Notater @@ -5495,10 +5443,6 @@ Sluttvolumet i primærgjæringskaret er %1. Required - - name - - Yield % Utbytte % @@ -5507,18 +5451,10 @@ Sluttvolumet i primærgjæringskaret er %1. Extras Ekstra - - origin - - Diastatic power - - supplier - - Notes Notater @@ -7911,26 +7847,6 @@ Sluttvolumet i primærgjæringskaret er %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -8036,34 +7952,14 @@ Sluttvolumet i primærgjæringskaret er %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -8080,14 +7976,6 @@ Sluttvolumet i primærgjæringskaret er %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -8100,50 +7988,26 @@ Sluttvolumet i primærgjæringskaret er %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_nl.ts b/translations/bt_nl.ts index 02568168..7b58755a 100644 --- a/translations/bt_nl.ts +++ b/translations/bt_nl.ts @@ -5252,22 +5252,10 @@ Het uiteindelijke volume in de hoofdvergisting is %1. Name Naam - - name - - Required - - boilSize_l - - - - batchSize_l - - Time Tijd @@ -5284,38 +5272,18 @@ Het uiteindelijke volume in de hoofdvergisting is %1. Evaporation rate (per hr) Verdampingssnelheid (per uur) - - evapRate_lHr - - Final top-up water Uiteindelijk top-up water - - topUpWater_l - - Kettle top-up water Ketel top-up water - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -5332,10 +5300,6 @@ Het uiteindelijke volume in de hoofdvergisting is %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water Water Kookpunt @@ -5352,26 +5316,10 @@ Het uiteindelijke volume in de hoofdvergisting is %1. Volume of mash tun Volume van het maisch vat - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes @@ -5547,10 +5495,6 @@ Het uiteindelijke volume in de hoofdvergisting is %1. Required - - name - - Yield % Rendement % @@ -5559,14 +5503,6 @@ Het uiteindelijke volume in de hoofdvergisting is %1. Extras Extras - - origin - - - - supplier - - Notes @@ -7832,26 +7768,6 @@ Het uiteindelijke volume in de hoofdvergisting is %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -7953,34 +7869,14 @@ Het uiteindelijke volume in de hoofdvergisting is %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -7997,14 +7893,6 @@ Het uiteindelijke volume in de hoofdvergisting is %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -8017,50 +7905,26 @@ Het uiteindelijke volume in de hoofdvergisting is %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_pl.ts b/translations/bt_pl.ts index dfab08a4..03c9ca67 100644 --- a/translations/bt_pl.ts +++ b/translations/bt_pl.ts @@ -5196,22 +5196,10 @@ Końcowa pojemność w fermentorze wyniesie %1. Name - - name - - Required - - boilSize_l - - - - batchSize_l - - Time Czas @@ -5228,38 +5216,18 @@ Końcowa pojemność w fermentorze wyniesie %1. Evaporation rate (per hr) Szybkość parowania (na godz.) - - evapRate_lHr - - Final top-up water Początkowa ilość wody w fermentorze - - topUpWater_l - - Kettle top-up water Początkowa ilość wody w kotle - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -5276,10 +5244,6 @@ Końcowa pojemność w fermentorze wyniesie %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water Temperatura wrzenia wody @@ -5296,26 +5260,10 @@ Końcowa pojemność w fermentorze wyniesie %1. Volume of mash tun Pojemność kadzi zaciernej - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes Notatki @@ -5495,10 +5443,6 @@ Końcowa pojemność w fermentorze wyniesie %1. Required - - name - - Yield % Ekstraktywność % @@ -5507,18 +5451,10 @@ Końcowa pojemność w fermentorze wyniesie %1. Extras Szczegóły - - origin - - Diastatic power - - supplier - - Notes Notatki @@ -7912,26 +7848,6 @@ Końcowa pojemność w fermentorze wyniesie %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -8037,34 +7953,14 @@ Końcowa pojemność w fermentorze wyniesie %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -8081,14 +7977,6 @@ Końcowa pojemność w fermentorze wyniesie %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -8101,50 +7989,26 @@ Końcowa pojemność w fermentorze wyniesie %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_pt.ts b/translations/bt_pt.ts index 0fed0766..9cd7bcb1 100644 --- a/translations/bt_pt.ts +++ b/translations/bt_pt.ts @@ -5230,22 +5230,10 @@ O volume final do fermentador primário é %1. Name Nome - - name - - Required - - boilSize_l - - - - batchSize_l - - Time Tempo @@ -5262,38 +5250,18 @@ O volume final do fermentador primário é %1. Evaporation rate (per hr) Taxa de evaporação por hora - - evapRate_lHr - - Final top-up water Água para encher até o fim - - topUpWater_l - - Kettle top-up water Água adicionada à panela - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -5310,10 +5278,6 @@ O volume final do fermentador primário é %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water Ponto de Ebolição da Água @@ -5330,26 +5294,10 @@ O volume final do fermentador primário é %1. Volume of mash tun Volume da panela de mosturação - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes @@ -5529,10 +5477,6 @@ O volume final do fermentador primário é %1. Required - - name - - Yield % Rendimento % @@ -5541,18 +5485,10 @@ O volume final do fermentador primário é %1. Extras Extras - - origin - - Diastatic power - - supplier - - Notes @@ -7958,26 +7894,6 @@ O volume final do fermentador primário é %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -8083,34 +7999,14 @@ O volume final do fermentador primário é %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -8127,14 +8023,6 @@ O volume final do fermentador primário é %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -8147,50 +8035,26 @@ O volume final do fermentador primário é %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_ru.ts b/translations/bt_ru.ts index fd148090..72417c6c 100644 --- a/translations/bt_ru.ts +++ b/translations/bt_ru.ts @@ -5262,22 +5262,10 @@ The final volume in the primary is %1. Name - - name - - Required - - boilSize_l - - - - batchSize_l - - Time Время @@ -5294,38 +5282,18 @@ The final volume in the primary is %1. Evaporation rate (per hr) Скорость испарения (в час) - - evapRate_lHr - - Final top-up water Доливаемая в сусловарник вода - - topUpWater_l - - Kettle top-up water Доливка воды в котёл - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -5342,10 +5310,6 @@ The final volume in the primary is %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water Точка кипения воды @@ -5362,26 +5326,10 @@ The final volume in the primary is %1. Volume of mash tun Объём заторника - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes @@ -5561,10 +5509,6 @@ The final volume in the primary is %1. Required - - name - - Yield % Выход % @@ -5573,18 +5517,10 @@ The final volume in the primary is %1. Extras Дополнительно - - origin - - Diastatic power - - supplier - - Notes @@ -7966,26 +7902,6 @@ The final volume in the primary is %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -8091,34 +8007,14 @@ The final volume in the primary is %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -8135,14 +8031,6 @@ The final volume in the primary is %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -8155,50 +8043,26 @@ The final volume in the primary is %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_sr.ts b/translations/bt_sr.ts index 27dcac13..295ff2f4 100644 --- a/translations/bt_sr.ts +++ b/translations/bt_sr.ts @@ -4945,22 +4945,10 @@ The final volume in the primary is %1. Name Назив - - name - - Required - - boilSize_l - - - - batchSize_l - - Time Трајање @@ -4977,38 +4965,18 @@ The final volume in the primary is %1. Evaporation rate (per hr) - - evapRate_lHr - - Final top-up water - - topUpWater_l - - Kettle top-up water - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -5025,10 +4993,6 @@ The final volume in the primary is %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water @@ -5045,26 +5009,10 @@ The final volume in the primary is %1. Volume of mash tun - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes Белешке @@ -5220,10 +5168,6 @@ The final volume in the primary is %1. Required - - name - - Yield % Принос % @@ -5232,18 +5176,10 @@ The final volume in the primary is %1. Extras - - origin - - Diastatic power - - supplier - - Notes Белешке @@ -7381,26 +7317,6 @@ The final volume in the primary is %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -7502,34 +7418,14 @@ The final volume in the primary is %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -7546,14 +7442,6 @@ The final volume in the primary is %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -7566,50 +7454,26 @@ The final volume in the primary is %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_sv.ts b/translations/bt_sv.ts index 7b318491..1edb8016 100644 --- a/translations/bt_sv.ts +++ b/translations/bt_sv.ts @@ -5246,22 +5246,10 @@ Primärens slutgiltiga volym är %1. Name Namn - - name - - Required - - boilSize_l - - - - batchSize_l - - Time Tid @@ -5278,38 +5266,18 @@ Primärens slutgiltiga volym är %1. Evaporation rate (per hr) Avdunstning (per timme) - - evapRate_lHr - - Final top-up water Avslutande uppfyllning Vatten - - topUpWater_l - - Kettle top-up water Kittel uppfyllning vatten - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -5326,10 +5294,6 @@ Primärens slutgiltiga volym är %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water Vattnets kokpunkt @@ -5346,26 +5310,10 @@ Primärens slutgiltiga volym är %1. Volume of mash tun Mäsktunnans volym - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes Anteckningar @@ -5545,10 +5493,6 @@ Primärens slutgiltiga volym är %1. Required - - name - - Yield % Ge % @@ -5557,18 +5501,10 @@ Primärens slutgiltiga volym är %1. Extras Extra - - origin - - Diastatic power - - supplier - - Notes Anteckningar @@ -7866,26 +7802,6 @@ Primärens slutgiltiga volym är %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -7987,34 +7903,14 @@ Primärens slutgiltiga volym är %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -8031,14 +7927,6 @@ Primärens slutgiltiga volym är %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -8051,50 +7939,26 @@ Primärens slutgiltiga volym är %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_tr.ts b/translations/bt_tr.ts index a2e0c759..da0d2ef0 100644 --- a/translations/bt_tr.ts +++ b/translations/bt_tr.ts @@ -4474,22 +4474,10 @@ The final volume in the primary is %1. Name - - name - - Required - - boilSize_l - - - - batchSize_l - - Time Süre @@ -4506,38 +4494,18 @@ The final volume in the primary is %1. Evaporation rate (per hr) - - evapRate_lHr - - Final top-up water - - topUpWater_l - - Kettle top-up water - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -4554,10 +4522,6 @@ The final volume in the primary is %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water @@ -4574,26 +4538,10 @@ The final volume in the primary is %1. Volume of mash tun - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes @@ -4725,10 +4673,6 @@ The final volume in the primary is %1. Required - - name - - Yield % @@ -4737,18 +4681,10 @@ The final volume in the primary is %1. Extras - - origin - - Diastatic power - - supplier - - Notes @@ -6802,26 +6738,6 @@ The final volume in the primary is %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -6923,34 +6839,14 @@ The final volume in the primary is %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -6967,14 +6863,6 @@ The final volume in the primary is %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -6987,50 +6875,26 @@ The final volume in the primary is %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/translations/bt_zh.ts b/translations/bt_zh.ts index 2ec1d60a..26f80f96 100644 --- a/translations/bt_zh.ts +++ b/translations/bt_zh.ts @@ -5160,22 +5160,10 @@ The final volume in the primary is %1. Name - - name - - Required - - boilSize_l - - - - batchSize_l - - Time 时间Time @@ -5192,38 +5180,18 @@ The final volume in the primary is %1. Evaporation rate (per hr) 蒸发率(每小时)Evaporation rate (per hr) - - evapRate_lHr - - Final top-up water 最终补足水Final top-up water - - topUpWater_l - - Kettle top-up water 水壶补足水Kettle top-up water - - topUpKettle_l - - Kettle to Fermenter Loss - - trubChillerLoss_l - - - - lauterDeadspace_l - - Physics @@ -5240,10 +5208,6 @@ The final volume in the primary is %1. Hop % Utilization - - grainAbsorption_LKg - - Boiling Point of Water 水的沸点 @@ -5260,26 +5224,10 @@ The final volume in the primary is %1. Volume of mash tun 糖化桶体积 - - hopUtilization_pct - - - - boilingPoint_c - - - - tunWeight_kg - - Specific Heat (Cal/(g*C)) - - tunSpecificHeat_calGC - - Notes 说明Notes @@ -5455,10 +5403,6 @@ The final volume in the primary is %1. Required - - name - - Yield % 率% @@ -5467,18 +5411,10 @@ The final volume in the primary is %1. Extras 额外Extras - - origin - - Diastatic power - - supplier - - Notes 说明Notes @@ -7860,26 +7796,6 @@ The final volume in the primary is %1. Required - - styleLetter - - - - styleGuide - - - - categoryNumber - - - - category - - - - name - - Ranges @@ -7985,34 +7901,14 @@ The final volume in the primary is %1. totalSalts - - nappm - - Ca - - so4ppm - - Mg - - cappm - - - - ph - - - - hco3ppm - - pH @@ -8029,14 +7925,6 @@ The final volume in the primary is %1. SO<sub>4</sub> - - mgppm - - - - clppm - - HCO<sub>3</sub> @@ -8049,50 +7937,26 @@ The final volume in the primary is %1. CaCl<sub>2</sub> - - cacl - - MgSO<sub>4</sub> - - mgso4 - - CaCO<sub>3</sub> - - caco3 - - NaCl - - nacl - - CaSO<sub>4<?sub> - - caso4 - - NaHCO<sub>3</sub> - - nahco3 - - Salts diff --git a/ui/brewNoteWidget.ui b/ui/brewNoteWidget.ui index b9ee8962..fb140edb 100644 --- a/ui/brewNoteWidget.ui +++ b/ui/brewNoteWidget.ui @@ -125,9 +125,6 @@ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - sg -
@@ -169,9 +166,6 @@ Volume of wort collected - - volumeIntoBK_l - @@ -213,9 +207,6 @@ Temperature of strike water before dough in - - strikeTemp_c - @@ -257,9 +248,6 @@ Temperature of mash before mash out - - mashFinTemp_c - @@ -319,9 +307,6 @@ Post boil gravity - - og - @@ -369,9 +354,6 @@ Volume of wort in BK after boil - - postBoilVolume_l - @@ -416,9 +398,6 @@ 16777215 - - volumeIntoFerm_l - @@ -463,9 +442,6 @@ Temperature of wort when yeast is pitched - - pitchTemp_c - @@ -522,9 +498,6 @@ Final gravity - - fg - @@ -563,9 +536,6 @@ Volume of beer into serving keg/bottles - - finalVolume_l - @@ -596,9 +566,6 @@ true - - fermentDate - @@ -640,9 +607,6 @@ Projected OG - - projOg - page_preboil @@ -806,168 +770,6 @@ - - - label_Sg - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_Sg - lineChanged(PreviousScaleInfo) - - - 70 - 55 - - - 131 - 55 - - - - - label_volIntoBk - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_volIntoBk - lineChanged(PreviousScaleInfo) - - - 58 - 81 - - - 131 - 81 - - - - - label_strikeTemp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_strikeTemp - lineChanged(PreviousScaleInfo) - - - 48 - 107 - - - 131 - 107 - - - - - label_mashFinTemp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_mashFinTemp - lineChanged(PreviousScaleInfo) - - - 51 - 133 - - - 131 - 133 - - - - - label_Og - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_Og - lineChanged(PreviousScaleInfo) - - - 125 - 81 - - - 187 - 81 - - - - - label_postBoilVol - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_postBoilVol - lineChanged(PreviousScaleInfo) - - - 92 - 107 - - - 187 - 107 - - - - - label_volIntoFerm - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_volIntoFerm - lineChanged(PreviousScaleInfo) - - - 76 - 133 - - - 187 - 133 - - - - - label_pitchTemp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_pitchTemp - lineChanged(PreviousScaleInfo) - - - 105 - 159 - - - 187 - 159 - - - - - label_Fg - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_Fg - lineChanged(PreviousScaleInfo) - - - 50 - 107 - - - 111 - 107 - - - - - label_finalVolume - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_finalVolume - lineChanged(PreviousScaleInfo) - - - 38 - 133 - - - 111 - 133 - - - - 10 diff --git a/ui/equipmentEditor.ui b/ui/equipmentEditor.ui index 724d16e7..dd83f34a 100644 --- a/ui/equipmentEditor.ui +++ b/ui/equipmentEditor.ui @@ -128,9 +128,6 @@ Name - - name - @@ -145,16 +142,10 @@ - - boilSize_l - - - batchSize_l - @@ -168,9 +159,6 @@ Time - - boilTime_min - @@ -228,9 +216,6 @@ - - evapRate_lHr - @@ -248,9 +233,6 @@ - - topUpWater_l - @@ -268,9 +250,6 @@ - - topUpKettle_l - @@ -288,9 +267,6 @@ - - trubChillerLoss_l - @@ -308,9 +284,6 @@ - - lauterDeadspace_l - @@ -349,9 +322,6 @@ - - grainAbsorption_LKg - @@ -404,16 +374,10 @@ Volume of mash tun - - tunVolume_l - - - hopUtilization_pct - @@ -431,9 +395,6 @@ - - boilingPoint_c - @@ -451,9 +412,6 @@ - - tunWeight_kg - @@ -465,9 +423,6 @@ - - tunSpecificHeat_calGC - @@ -609,182 +564,4 @@ - - - label_tunWeight - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_tunWeight - lineChanged(PreviousScaleInfo) - - - 120 - 101 - - - 269 - 101 - - - - - label_boilTime - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_boilTime - lineChanged(PreviousScaleInfo) - - - 119 - 68 - - - 258 - 68 - - - - - label_batchSize - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_batchSize - lineChanged(PreviousScaleInfo) - - - 121 - 101 - - - 271 - 101 - - - - - label_boilSize - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_boilSize - lineChanged(PreviousScaleInfo) - - - 121 - 134 - - - 271 - 134 - - - - - label_evaporationRate - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_evaporationRate - lineChanged(PreviousScaleInfo) - - - 119 - 101 - - - 258 - 101 - - - - - label_topUpKettle - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_topUpKettle - lineChanged(PreviousScaleInfo) - - - 119 - 134 - - - 258 - 134 - - - - - label_topUpWater - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_topUpWater - lineChanged(PreviousScaleInfo) - - - 119 - 167 - - - 258 - 167 - - - - - label_boilingPoint - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_boilingPoint - lineChanged(PreviousScaleInfo) - - - 119 - 233 - - - 258 - 233 - - - - - label_tunVolume - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_tunWeight - lineChanged(PreviousScaleInfo) - - - 120 - 68 - - - 269 - 101 - - - - - label_trubChillerLoss - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_trubChillerLoss - lineChanged(PreviousScaleInfo) - - - 120 - 192 - - - 269 - 192 - - - - - label_lauterDeadspace - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_lauterDeadspace - lineChanged(PreviousScaleInfo) - - - 120 - 225 - - - 269 - 225 - - - - diff --git a/ui/fermentableEditor.ui b/ui/fermentableEditor.ui index 798509ad..bf7b9526 100644 --- a/ui/fermentableEditor.ui +++ b/ui/fermentableEditor.ui @@ -166,9 +166,6 @@ Name - - name - @@ -243,9 +240,6 @@ Amount in inventory - - inventory - @@ -281,9 +275,6 @@ Lovibond rating - - color_srm - @@ -316,9 +307,6 @@ Yield as compared to glucose - - yield_pct - @@ -345,9 +333,6 @@ Moisture percentage by mass - - moisture_pct - @@ -377,9 +362,6 @@ Bitterness of pre-hopped extracts - - ibuGalPerLb - @@ -409,9 +391,6 @@ Origin - - origin - @@ -464,9 +443,6 @@ Diastatic power - - diastaticPower_lintner - @@ -509,9 +485,6 @@ Supplier - - supplier - @@ -531,9 +504,6 @@ Maximum recommended percentage of total grist - - maxInBatch_pct - @@ -566,9 +536,6 @@ Protein percentage by mass - - protein_pct - @@ -598,9 +565,6 @@ Yield difference between coarse and fine grind - - coarseFineDiff_pct - @@ -745,70 +709,4 @@ - - - label_inventory - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_inventory - lineChanged(PreviousScaleInfo) - - - 66 - 220 - - - 177 - 220 - - - - - label_color - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_color - lineChanged(PreviousScaleInfo) - - - 67 - 133 - - - 169 - 133 - - - - - label_diastaticPower - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_diastaticPower - cut() - - - 301 - 46 - - - 393 - 49 - - - - - label_diastaticPower - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_diastaticPower - lineChanged(PreviousScaleInfo) - - - 346 - 52 - - - 411 - 48 - - - - diff --git a/ui/hopEditor.ui b/ui/hopEditor.ui index 6264785e..1f4d2c10 100644 --- a/ui/hopEditor.ui +++ b/ui/hopEditor.ui @@ -89,9 +89,6 @@ Alpha acids as percent by mass - - alpha_pct - @@ -191,9 +188,6 @@ Amount in inventory - - inventory - @@ -240,9 +234,6 @@ Year - - year - @@ -315,9 +306,6 @@ Time - - time_min - @@ -348,9 +336,6 @@ Beta acids as percent by mass - - beta_pct - @@ -374,9 +359,6 @@ Hop Stability/Storage index - - hsi_pct - @@ -406,9 +388,6 @@ Humulene - - humulene_pct - @@ -438,9 +417,6 @@ Linalool - - linalool_pct - @@ -470,9 +446,6 @@ Caryophyllene % - - caryophyllene_pct - @@ -502,9 +475,6 @@ Limonene - - limonene_pct - @@ -534,9 +504,6 @@ Cohumulone - - cohumulone_pct - @@ -566,9 +533,6 @@ Nerol - - nerol_pct - @@ -598,9 +562,6 @@ Myrcene - - myrcene_pct - @@ -630,9 +591,6 @@ Pinene - - pinene_pct - @@ -662,9 +620,6 @@ Farnesene - - farnesene_pct - @@ -694,9 +649,6 @@ Polyphenols - - polyphenols_pct - @@ -726,9 +678,6 @@ Geraniol - - geraniol_pct - @@ -758,9 +707,6 @@ Xanthohumol - - xanthohumol_pct - @@ -790,9 +736,6 @@ β-pinene - - b_pinene_pct - @@ -822,9 +765,6 @@ Total Oil ml/100g - - total_oil_ml_per_100g - diff --git a/ui/mainWindow.ui b/ui/mainWindow.ui index 2f07245b..c61da15c 100644 --- a/ui/mainWindow.ui +++ b/ui/mainWindow.ui @@ -402,9 +402,6 @@ true - - boilGrav - @@ -468,9 +465,6 @@ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - boilTime_min - @@ -515,9 +509,6 @@ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - efficiency_pct - @@ -651,9 +642,6 @@ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - batchSize_l - @@ -758,9 +746,6 @@ Qt::AlignJustify|Qt::AlignVCenter - - boilSize_l - @@ -837,9 +822,6 @@ Color - - color_srm - @@ -879,9 +861,6 @@ OG - - og - @@ -892,9 +871,6 @@ FG - - fg - diff --git a/ui/mashDesigner.ui b/ui/mashDesigner.ui index 13cdf533..3daf2137 100644 --- a/ui/mashDesigner.ui +++ b/ui/mashDesigner.ui @@ -110,9 +110,6 @@ 16777215 - - stepTemp_c - @@ -136,9 +133,6 @@ 16777215 - - stepTime_min - @@ -519,38 +513,4 @@ pushButton_finish - - - label_targetTemp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_temp - lineChanged(PreviousScaleInfo) - - - 39 - 98 - - - 131 - 98 - - - - - label_stepTime - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_time - lineChanged(PreviousScaleInfo) - - - 56 - 126 - - - 94 - 125 - - - - diff --git a/ui/mashEditor.ui b/ui/mashEditor.ui index 85d22308..76a7fa04 100644 --- a/ui/mashEditor.ui +++ b/ui/mashEditor.ui @@ -99,9 +99,6 @@ Initial grain temp - - grainTemp_c - @@ -144,9 +141,6 @@ Sparge temp target - - spargeTemp_c - @@ -186,9 +180,6 @@ Sparge pH - - ph - @@ -287,9 +278,6 @@ Initial tun temp - - tunTemp_c - @@ -351,9 +339,6 @@ Tun mass - - tunWeight_kg - @@ -393,9 +378,6 @@ Tun specific heat (cal/(g*K)) - - tunSpecificHeat_calGC - @@ -485,69 +467,5 @@ - - label_grainTemp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_grainTemp - lineChanged(PreviousScaleInfo) - - - 87 - 42 - - - 223 - 42 - - - - - label_spargeTemp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_spargeTemp - lineChanged(PreviousScaleInfo) - - - 87 - 68 - - - 223 - 68 - - - - - label_tunTemp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_tunTemp - lineChanged(PreviousScaleInfo) - - - 326 - 45 - - - 418 - 45 - - - - - label_tunMass - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_tunMass - lineChanged(PreviousScaleInfo) - - - 326 - 96 - - - 418 - 96 - - - diff --git a/ui/mashStepEditor.ui b/ui/mashStepEditor.ui index c380e4d7..758b02a8 100644 --- a/ui/mashStepEditor.ui +++ b/ui/mashStepEditor.ui @@ -163,9 +163,6 @@ Target temp. of this step - - stepTemp_c - @@ -214,9 +211,6 @@ Amount of water to infuse - - infuseAmount_l - @@ -259,9 +253,6 @@ Temperature of infusion water - - infuseTemp_c - @@ -317,9 +308,6 @@ Amount of mash to decoct - - decoctionAmount_l - @@ -368,9 +356,6 @@ Time to conduct the step - - stepTime_min - @@ -419,9 +404,6 @@ Lag time - - rampTime_min - @@ -470,9 +452,6 @@ Final temp. of this step - - endTemp_c - @@ -543,117 +522,5 @@ - - label_stepTemp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_stepTemp - lineChanged(PreviousScaleInfo) - - - 29 - 85 - - - 155 - 88 - - - - - label_infuseAmount - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_infuseAmount - lineChanged(PreviousScaleInfo) - - - 53 - 126 - - - 178 - 126 - - - - - label_infuseTemp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_infuseTemp - lineChanged(PreviousScaleInfo) - - - 40 - 159 - - - 172 - 160 - - - - - label_decoctionAmount - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_decoctionAmount - lineChanged(PreviousScaleInfo) - - - 310 - 26 - - - 415 - 24 - - - - - label_endTemp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_endTemp - lineChanged(PreviousScaleInfo) - - - 310 - 133 - - - 406 - 133 - - - - - label_stepTime - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_stepTime - lineChanged(PreviousScaleInfo) - - - 282 - 57 - - - 402 - 61 - - - - - label_rampTime - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_rampTime - lineChanged(PreviousScaleInfo) - - - 301 - 96 - - - 403 - 93 - - - diff --git a/ui/miscEditor.ui b/ui/miscEditor.ui index 51d6752e..858d8982 100644 --- a/ui/miscEditor.ui +++ b/ui/miscEditor.ui @@ -56,9 +56,6 @@ Time - - time - @@ -277,9 +274,6 @@ Amount in inventory - - inventory - diff --git a/ui/namedMashEditor.ui b/ui/namedMashEditor.ui index 2e671bec..34a9f378 100644 --- a/ui/namedMashEditor.ui +++ b/ui/namedMashEditor.ui @@ -617,70 +617,4 @@ - - - label_grainTemp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_grainTemp - lineChanged(PreviousScaleInfo) - - - 90 - 74 - - - 259 - 76 - - - - - label_spargeTemp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_spargeTemp - lineChanged(PreviousScaleInfo) - - - 53 - 102 - - - 312 - 105 - - - - - label_tunTemp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_tunTemp - lineChanged(PreviousScaleInfo) - - - 437 - 80 - - - 593 - 80 - - - - - label_tunMass - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_tunMass - lineChanged(PreviousScaleInfo) - - - 387 - 131 - - - 633 - 133 - - - - diff --git a/ui/ogAdjuster.ui b/ui/ogAdjuster.ui index 163cbf7a..a2cb071a 100644 --- a/ui/ogAdjuster.ui +++ b/ui/ogAdjuster.ui @@ -116,9 +116,6 @@ Temperature of SG reading - - temperature - @@ -155,9 +152,6 @@ Temp to which the hydrometer is calibrated - - calTemp - @@ -254,9 +248,6 @@ Plato (percent by mass of equivalent sucrose) - - plato - Plato @@ -332,9 +323,6 @@ Measured pre-boil volume - - volume - @@ -445,9 +433,6 @@ true - - add - @@ -499,9 +484,6 @@ true - - batchSize - @@ -591,86 +573,4 @@ - - - label_temp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_temp - lineChanged(PreviousScaleInfo) - - - 30 - 86 - - - 101 - 85 - - - - - label_calTemp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_calTemp - lineChanged(PreviousScaleInfo) - - - 55 - 121 - - - 126 - 121 - - - - - label_batchSize - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_batchSize - lineChanged(PreviousScaleInfo) - - - 411 - 101 - - - 537 - 101 - - - - - label_add - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_add - lineChanged(PreviousScaleInfo) - - - 411 - 73 - - - 537 - 73 - - - - - label_volume - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_volume - lineChanged(PreviousScaleInfo) - - - 174 - 155 - - - 268 - 155 - - - - diff --git a/ui/pitchDialog.ui b/ui/pitchDialog.ui index 48531316..756f94da 100644 --- a/ui/pitchDialog.ui +++ b/ui/pitchDialog.ui @@ -61,9 +61,6 @@ Volume of wort - - wortVol - @@ -96,9 +93,6 @@ Starting gravity of the wort - - og - @@ -200,9 +194,6 @@ 0 - - productionDate - @@ -402,9 +393,6 @@ true - - yeast - @@ -434,9 +422,6 @@ true - - starterVol - @@ -482,70 +467,4 @@ lineEdit_starterVol - - - label_vol - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_vol - lineChanged(PreviousScaleInfo) - - - 178 - 44 - - - 271 - 44 - - - - - label_og - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_OG - lineChanged(PreviousScaleInfo) - - - 203 - 70 - - - 271 - 70 - - - - - label_starterVol - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_starterVol - lineChanged(PreviousScaleInfo) - - - 464 - 122 - - - 581 - 122 - - - - - label_yeast - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_yeast - lineChanged(PreviousScaleInfo) - - - 478 - 96 - - - 581 - 96 - - - - diff --git a/ui/primingDialog.ui b/ui/primingDialog.ui index af0e7555..dbe1b0a2 100644 --- a/ui/primingDialog.ui +++ b/ui/primingDialog.ui @@ -55,9 +55,6 @@ Amount of beer to prime - - beerVol - @@ -87,9 +84,6 @@ Temp of the beer - - beerTemp - @@ -211,9 +205,6 @@ true - - primeMass - @@ -285,54 +276,4 @@ pushButton_calculate - - - label_beerVol - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_beerVol - lineChanged(PreviousScaleInfo) - - - 68 - 44 - - - 169 - 44 - - - - - label_temp - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_temp - lineChanged(PreviousScaleInfo) - - - 80 - 70 - - - 169 - 70 - - - - - label_output - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_output - lineChanged(PreviousScaleInfo) - - - 258 - 44 - - - 349 - 44 - - - - diff --git a/ui/strikeWaterDialog.ui b/ui/strikeWaterDialog.ui index f53ad939..6c188483 100644 --- a/ui/strikeWaterDialog.ui +++ b/ui/strikeWaterDialog.ui @@ -58,9 +58,6 @@ 0 - - grainTempVal - @@ -84,9 +81,6 @@ 0 - - targetMashVal - @@ -113,9 +107,6 @@ 1 - - grainWeightInitVal - @@ -139,9 +130,6 @@ 0 - - waterVolumeVal - @@ -180,9 +168,6 @@ 0 - - mashVolVal - @@ -206,9 +191,6 @@ 0 - - grainWeight - @@ -232,9 +214,6 @@ 0 - - actualMashVal - @@ -258,9 +237,6 @@ 0 - - targetMashInfVal - @@ -284,9 +260,6 @@ 0 - - infusionWaterVal - @@ -366,9 +339,6 @@ true - - initialResultTxt - @@ -411,9 +381,6 @@ true - - mashResultTxt - diff --git a/ui/styleEditor.ui b/ui/styleEditor.ui index 08f8f915..4c5aa7f7 100644 --- a/ui/styleEditor.ui +++ b/ui/styleEditor.ui @@ -138,9 +138,6 @@ Style letter - - styleLetter - @@ -166,9 +163,6 @@ Style guide - - styleGuide - @@ -194,9 +188,6 @@ Category number - - categoryNumber - @@ -222,9 +213,6 @@ Category - - category - @@ -378,9 +366,6 @@ Name - - name - @@ -436,9 +421,6 @@ 0 - - ibuMax - @@ -455,9 +437,6 @@ 0 - - colorMin_srm - @@ -474,9 +453,6 @@ 0 - - abvMax_pct - @@ -500,9 +476,6 @@ 0 - - carbMin_vol - @@ -513,9 +486,6 @@ FG - - fg - @@ -539,9 +509,6 @@ 0 - - ogMin - @@ -558,9 +525,6 @@ 0 - - ibuMin - @@ -577,9 +541,6 @@ 0 - - ogMax - @@ -596,9 +557,6 @@ 0 - - colorMax_srm - @@ -615,9 +573,6 @@ 0 - - fgMin - @@ -641,9 +596,6 @@ 0 - - fgMax - @@ -654,9 +606,6 @@ Color (SRM) - - color_srm - @@ -673,9 +622,6 @@ 0 - - abvMin_pct - @@ -686,9 +632,6 @@ OG - - og - @@ -705,9 +648,6 @@ 0 - - carbMax_vol - @@ -932,102 +872,4 @@ - - - label_og - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_ogMin - lineChanged(PreviousScaleInfo) - - - 43 - 284 - - - 128 - 284 - - - - - label_og - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_ogMax - lineChanged(PreviousScaleInfo) - - - 43 - 284 - - - 232 - 284 - - - - - label_fg - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_fgMin - lineChanged(PreviousScaleInfo) - - - 43 - 310 - - - 128 - 310 - - - - - label_fg - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_fgMax - lineChanged(PreviousScaleInfo) - - - 43 - 310 - - - 232 - 310 - - - - - label_color - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_colorMin - lineChanged(PreviousScaleInfo) - - - 43 - 362 - - - 128 - 362 - - - - - label_color - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_colorMax - lineChanged(PreviousScaleInfo) - - - 43 - 362 - - - 232 - 362 - - - - diff --git a/ui/waterDialog.ui b/ui/waterDialog.ui index 97095d1f..c7268c24 100644 --- a/ui/waterDialog.ui +++ b/ui/waterDialog.ui @@ -164,9 +164,6 @@ - - nappm - @@ -178,9 +175,6 @@ - - so4ppm - @@ -192,23 +186,14 @@ - - cappm - - - ph - - - hco3ppm - @@ -241,16 +226,10 @@ - - mgppm - - - clppm - @@ -287,9 +266,6 @@ - - cacl - @@ -307,9 +283,6 @@ - - mgso4 - @@ -327,9 +300,6 @@ - - caco3 - @@ -347,9 +317,6 @@ - - nacl - @@ -367,9 +334,6 @@ - - caso4 - @@ -387,9 +351,6 @@ - - nahco3 - @@ -563,101 +524,5 @@ - - label_totalcacl2 - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - btDigit_totalcacl2 - displayChanged(PreviousScaleInfo) - - - 70 - 433 - - - 160 - 433 - - - - - label_totalcaco3 - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - btDigit_totalcaco3 - displayChanged(PreviousScaleInfo) - - - 70 - 469 - - - 160 - 469 - - - - - label_totalcaso4 - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - btDigit_totalcaso4 - displayChanged(PreviousScaleInfo) - - - 70 - 506 - - - 160 - 506 - - - - - label_totalmgso4 - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - btDigit_totalmgso4 - displayChanged(PreviousScaleInfo) - - - 251 - 433 - - - 341 - 433 - - - - - label_totalnacl - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - btDigit_totalnacl - displayChanged(PreviousScaleInfo) - - - 251 - 469 - - - 341 - 469 - - - - - label_totalnahco3 - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - btDigit_totalnahco3 - displayChanged(PreviousScaleInfo) - - - 251 - 506 - - - 341 - 506 - - - diff --git a/ui/yeastEditor.ui b/ui/yeastEditor.ui index cf5a0faf..a6876516 100644 --- a/ui/yeastEditor.ui +++ b/ui/yeastEditor.ui @@ -375,9 +375,6 @@ Min temp - - minTemperature_c - @@ -397,9 +394,6 @@ Apparent attenuation as percentage of OG points - - attenuation_pct - @@ -471,9 +465,6 @@ Max temp - - maxTemperature_c - @@ -661,38 +652,4 @@ - - - label_minTemperature - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_minTemperature - lineChanged(PreviousScaleInfo) - - - 46 - 251 - - - 143 - 251 - - - - - label_maxTemperature - changedSystemOfMeasurementOrScale(PreviousScaleInfo) - lineEdit_maxTemperature - lineChanged(PreviousScaleInfo) - - - 46 - 275 - - - 143 - 275 - - - - From 864029305f1ee475cabdb67ff31591c8f01c5dee Mon Sep 17 00:00:00 2001 From: Matt Young Date: Thu, 27 Apr 2023 19:09:59 +0200 Subject: [PATCH 22/42] Compile fixes --- src/RadarChart.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/RadarChart.cpp b/src/RadarChart.cpp index 47299d22..7de29266 100644 --- a/src/RadarChart.cpp +++ b/src/RadarChart.cpp @@ -22,11 +22,19 @@ // C++20 introduces std::numbers::pi, which has better cross-platform support than the M_PI constant in cmath. However, // older versions of GCC (eg as shipped with Ubuntu 20.04 LTS) do not ship with the new header. // -#if defined(__GNUC__) && (__GNUC__ < 10) +#if defined(__linux__ ) && defined(__GNUC__) && (__GNUC__ < 10) + // I hope this is allowed. We'll be able to get rid of it once we stop needing to support old versions of GCC -constexpr double std::numbers::pi = M_PI; +namespace std { + namespace numbers { + constexpr double pi = M_PI; + } +} + #else + #include // For std::numbers::pi + #endif #include From 74b238fda0085586ce922c08d5ce8b0cd5e8c284 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sat, 29 Apr 2023 10:49:20 +0200 Subject: [PATCH 23/42] Fix for old Linux builds --- src/measurement/SucroseConversion.cpp | 4 +-- src/model/Fermentable.cpp | 4 +-- src/model/Fermentable.h | 10 +++--- src/model/Recipe.cpp | 40 +++++++++++------------ src/tableModels/FermentableTableModel.cpp | 6 ++-- 5 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/measurement/SucroseConversion.cpp b/src/measurement/SucroseConversion.cpp index 31bf2949..24238fc5 100644 --- a/src/measurement/SucroseConversion.cpp +++ b/src/measurement/SucroseConversion.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * measurement/SucroseConversion.cpp is part of Brewken, and is copyright the following authors 2022: + * measurement/SucroseConversion.cpp is part of Brewken, and is copyright the following authors 2022-2023: * • Matt Young * * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -41,7 +41,7 @@ // I found to avoid copying this data on to the heap (which would be unnecessary since it's const and known at // compile time). // -Measurement::SucroseConversion constexpr Measurement::sucroseConversions[] = { +Measurement::SucroseConversion const Measurement::sucroseConversions[] = { // Refractive Index at 20°C || % sucrose or degree Brix || Apparent specific gravity @ 20/20 °C { 1.3330, 0.0, 1.00000 }, { 1.3331, 0.1, 1.00039 }, // The PDF has this as 0.0 Brix, but I think that's clearly a typo diff --git a/src/model/Fermentable.cpp b/src/model/Fermentable.cpp index 403fbdba..b144c388 100644 --- a/src/model/Fermentable.cpp +++ b/src/model/Fermentable.cpp @@ -43,8 +43,8 @@ std::array const Fermentable::allTypes { Fermentable::Type::Other_Adjunct }; -// Note that Hop::typeStringMapping and Hop::FormMapping are as defined by BeerJSON, but we also use them for the DB and -// for the UI. We can't use them for BeerXML as it only supports subsets of these types. +// Note that Fermentable::typeStringMapping and Fermentable::FormMapping are as defined by BeerJSON, but we also use +// them for the DB and for the UI. We can't use them for BeerXML as it only supports subsets of these types. EnumStringMapping const Fermentable::typeStringMapping { {"dry extract", Fermentable::Type::Dry_Extract}, {"extract", Fermentable::Type::Extract}, diff --git a/src/model/Fermentable.h b/src/model/Fermentable.h index a4268b55..72102636 100644 --- a/src/model/Fermentable.h +++ b/src/model/Fermentable.h @@ -123,6 +123,11 @@ class Fermentable : public NamedEntityWithInventory { */ static EnumStringMapping const typeStringMapping; + /*! + * \brief Localised names of \c Fermentable::Type values suitable for displaying to the end user + */ + static QMap const typeDisplayNames; + /** * \brief An additional classification of \c Fermentable introduced in BeerJSON * @@ -156,11 +161,6 @@ class Fermentable : public NamedEntityWithInventory { */ static EnumStringMapping const grainGroupStringMapping; - /*! - * \brief Localised names of \c Fermentable::Type values suitable for displaying to the end user - */ - static QMap const typeDisplayNames; - /** * \brief Mapping of names to types for the Qt properties of this class. See \c NamedEntity::typeLookup for more * info. diff --git a/src/model/Recipe.cpp b/src/model/Recipe.cpp index 6a4b0890..551b64ea 100644 --- a/src/model/Recipe.cpp +++ b/src/model/Recipe.cpp @@ -2704,11 +2704,11 @@ QList Recipe::getReagents(QList hops, bool firstWort) { QString tmp; QList reagents; - for (int i = 0; i < hops.size(); ++i) { - if (firstWort && (hops[i]->use() == Hop::Use::First_Wort)) { + for (int ii = 0; ii < hops.size(); ++ii) { + if (firstWort && (hops[ii]->use() == Hop::Use::First_Wort)) { tmp = QString("%1 %2,") - .arg(Measurement::displayAmount(Measurement::Amount{hops[i]->amount_kg(), Measurement::Units::kilograms})) - .arg(hops[i]->name()); + .arg(Measurement::displayAmount(Measurement::Amount{hops[ii]->amount_kg(), Measurement::Units::kilograms})) + .arg(hops[ii]->name()); reagents.append(tmp); } } @@ -2718,20 +2718,20 @@ QList Recipe::getReagents(QList hops, bool firstWort) { QList Recipe::getReagents(QList< std::shared_ptr > msteps) { QList reagents; - for (int i = 0; i < msteps.size(); ++i) { - if (!msteps[i]->isInfusion()) { + for (int ii = 0; ii < msteps.size(); ++ii) { + if (!msteps[ii]->isInfusion()) { continue; } QString tmp; - if (i + 1 < msteps.size()) { + if (ii + 1 < msteps.size()) { tmp = tr("%1 water to %2, ") - .arg(Measurement::displayAmount(Measurement::Amount{msteps[i]->infuseAmount_l(), Measurement::Units::liters})) - .arg(Measurement::displayAmount(Measurement::Amount{msteps[i]->infuseTemp_c(), Measurement::Units::celsius})); + .arg(Measurement::displayAmount(Measurement::Amount{msteps[ii]->infuseAmount_l(), Measurement::Units::liters})) + .arg(Measurement::displayAmount(Measurement::Amount{msteps[ii]->infuseTemp_c(), Measurement::Units::celsius})); } else { tmp = tr("%1 water to %2 ") - .arg(Measurement::displayAmount(Measurement::Amount{msteps[i]->infuseAmount_l(), Measurement::Units::liters})) - .arg(Measurement::displayAmount(Measurement::Amount{msteps[i]->infuseTemp_c(), Measurement::Units::celsius})); + .arg(Measurement::displayAmount(Measurement::Amount{msteps[ii]->infuseAmount_l(), Measurement::Units::liters})) + .arg(Measurement::displayAmount(Measurement::Amount{msteps[ii]->infuseTemp_c(), Measurement::Units::celsius})); } reagents.append(tmp); } @@ -2745,26 +2745,26 @@ QStringList Recipe::getReagents(QList salts, Salt::WhenToAdd wanted) { QString tmp; QStringList reagents = QStringList(); - for (int i = 0; i < salts.size(); ++i) { - Salt::WhenToAdd what = salts[i]->whenToAdd(); - Measurement::Unit const & rightUnit = salts[i]->amountIsWeight() ? Measurement::Units::kilograms : Measurement::Units::liters; + for (int ii = 0; ii < salts.size(); ++ii) { + Salt::WhenToAdd what = salts[ii]->whenToAdd(); + Measurement::Unit const & rightUnit = salts[ii]->amountIsWeight() ? Measurement::Units::kilograms : Measurement::Units::liters; if (what == wanted) { tmp = tr("%1 %2, ") - .arg(Measurement::displayAmount(Measurement::Amount{salts[i]->amount(), rightUnit})) - .arg(salts[i]->name()); + .arg(Measurement::displayAmount(Measurement::Amount{salts[ii]->amount(), rightUnit})) + .arg(salts[ii]->name()); } else if (what == Salt::WhenToAdd::EQUAL) { tmp = tr("%1 %2, ") - .arg(Measurement::displayAmount(Measurement::Amount{salts[i]->amount(), rightUnit})) - .arg(salts[i]->name()); + .arg(Measurement::displayAmount(Measurement::Amount{salts[ii]->amount(), rightUnit})) + .arg(salts[ii]->name()); } else if (what == Salt::WhenToAdd::RATIO) { double ratio = 1.0; if (wanted == Salt::WhenToAdd::SPARGE) { ratio = mash()->totalSpargeAmount_l() / mash()->totalInfusionAmount_l(); } - double amt = salts[i]->amount() * ratio; + double amt = salts[ii]->amount() * ratio; tmp = tr("%1 %2, ") .arg(Measurement::displayAmount(Measurement::Amount{amt, rightUnit})) - .arg(salts[i]->name()); + .arg(salts[ii]->name()); } else { continue; } diff --git a/src/tableModels/FermentableTableModel.cpp b/src/tableModels/FermentableTableModel.cpp index 2fb0a9c2..d90ea39d 100644 --- a/src/tableModels/FermentableTableModel.cpp +++ b/src/tableModels/FermentableTableModel.cpp @@ -89,9 +89,9 @@ FermentableTableModel::FermentableTableModel(QTableView* parent, bool editable) { SMART_COLUMN_HEADER_DEFN(FermentableTableModel, Name , tr("Name" ), NonPhysicalQuantity::String ), SMART_COLUMN_HEADER_DEFN(FermentableTableModel, Type , tr("Type" ), NonPhysicalQuantity::String ), - SMART_COLUMN_HEADER_DEFN(FermentableTableModel, Amount , tr("Amount" ), Measurement::PhysicalQuantity::Mass ), - SMART_COLUMN_HEADER_DEFN(FermentableTableModel, Inventory, tr("Amount Type"), NonPhysicalQuantity::Bool ), - SMART_COLUMN_HEADER_DEFN(FermentableTableModel, IsWeight , tr("Inventory" ), Measurement::PhysicalQuantity::Mass ), + SMART_COLUMN_HEADER_DEFN(FermentableTableModel, Amount , tr("Amount" ), Measurement::PhysicalQuantity::Mass ), // .:TODO:. Mass or Volume + SMART_COLUMN_HEADER_DEFN(FermentableTableModel, IsWeight , tr("Amount Type"), NonPhysicalQuantity::Bool ), + SMART_COLUMN_HEADER_DEFN(FermentableTableModel, Inventory, tr("Inventory" ), Measurement::PhysicalQuantity::Mass ), // .:TODO:. Mass or Volume SMART_COLUMN_HEADER_DEFN(FermentableTableModel, IsMashed , tr("Method" ), NonPhysicalQuantity::String ), SMART_COLUMN_HEADER_DEFN(FermentableTableModel, AfterBoil, tr("Addition" ), NonPhysicalQuantity::String ), SMART_COLUMN_HEADER_DEFN(FermentableTableModel, Yield , tr("Yield %" ), NonPhysicalQuantity::Percentage), From 3dbea01ff64041fdc17d5f7cb9981a061150459a Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sun, 30 Apr 2023 02:42:32 +0200 Subject: [PATCH 24/42] Small fix --- src/tableModels/FermentableTableModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tableModels/FermentableTableModel.cpp b/src/tableModels/FermentableTableModel.cpp index d90ea39d..9e21efef 100644 --- a/src/tableModels/FermentableTableModel.cpp +++ b/src/tableModels/FermentableTableModel.cpp @@ -90,8 +90,8 @@ FermentableTableModel::FermentableTableModel(QTableView* parent, bool editable) SMART_COLUMN_HEADER_DEFN(FermentableTableModel, Name , tr("Name" ), NonPhysicalQuantity::String ), SMART_COLUMN_HEADER_DEFN(FermentableTableModel, Type , tr("Type" ), NonPhysicalQuantity::String ), SMART_COLUMN_HEADER_DEFN(FermentableTableModel, Amount , tr("Amount" ), Measurement::PhysicalQuantity::Mass ), // .:TODO:. Mass or Volume - SMART_COLUMN_HEADER_DEFN(FermentableTableModel, IsWeight , tr("Amount Type"), NonPhysicalQuantity::Bool ), SMART_COLUMN_HEADER_DEFN(FermentableTableModel, Inventory, tr("Inventory" ), Measurement::PhysicalQuantity::Mass ), // .:TODO:. Mass or Volume + SMART_COLUMN_HEADER_DEFN(FermentableTableModel, IsWeight , tr("Amount Type"), NonPhysicalQuantity::Bool ), SMART_COLUMN_HEADER_DEFN(FermentableTableModel, IsMashed , tr("Method" ), NonPhysicalQuantity::String ), SMART_COLUMN_HEADER_DEFN(FermentableTableModel, AfterBoil, tr("Addition" ), NonPhysicalQuantity::String ), SMART_COLUMN_HEADER_DEFN(FermentableTableModel, Yield , tr("Yield %" ), NonPhysicalQuantity::Percentage), From 9d602b48e6b367aed897ad34745a85ae6fe96747 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sun, 30 Apr 2023 16:08:20 +0200 Subject: [PATCH 25/42] Fix handling of optional fields in SmartField etc --- meson.build | 9 +- src/CMakeLists.txt | 4 +- src/EquipmentEditor.cpp | 6 +- src/FermentableEditor.cpp | 12 +- src/HopEditor.cpp | 50 +++---- src/MainWindow.cpp | 4 +- src/StyleEditor.cpp | 8 +- src/YeastEditor.cpp | 6 +- src/measurement/Measurement.h | 5 +- src/model/Hop.cpp | 30 ++--- src/tableModels/BtTableModel.cpp | 2 +- src/utils/TypeLookup.h | 31 ++++- src/{ => widgets}/SmartAmounts.cpp | 6 +- src/{ => widgets}/SmartAmounts.h | 2 +- src/widgets/SmartDigitWidget.h | 2 +- src/{ => widgets}/SmartField.cpp | 203 +++++++++++++++-------------- src/{ => widgets}/SmartField.h | 50 ++++--- src/widgets/SmartLabel.cpp | 4 +- src/widgets/SmartLabel.h | 4 +- src/widgets/SmartLineEdit.cpp | 11 +- src/widgets/SmartLineEdit.h | 4 +- 21 files changed, 251 insertions(+), 202 deletions(-) rename src/{ => widgets}/SmartAmounts.cpp (98%) rename src/{ => widgets}/SmartAmounts.h (99%) rename src/{ => widgets}/SmartField.cpp (84%) rename src/{ => widgets}/SmartField.h (89%) diff --git a/meson.build b/meson.build index ef88fc47..ed27f7ee 100644 --- a/meson.build +++ b/meson.build @@ -16,9 +16,8 @@ #----------------------------------------------------------------------------------------------------------------------- # -# ⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ -# ⭐⭐⭐ THIS IS EXPERIMENTAL - YOU CAN ALSO STILL USE TRIED-AND-TESTED CMAKE TO BUILD THE PRODUCT -# ⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ +# NB: This is now the primary way of building and packaging the software. You can also still CMake to compile the +# product and install it locally, but we no longer support using CMake to do packaging. # # STEP 1: Ensure Python is installed: # ----------------------------------- @@ -690,8 +689,6 @@ commonSourceFiles = files([ 'src/RefractoDialog.cpp', 'src/ScaleRecipeTool.cpp', 'src/SimpleUndoableUpdate.cpp', - 'src/SmartAmounts.cpp', - 'src/SmartField.cpp', 'src/StrikeWaterDialog.cpp', 'src/StyleButton.cpp', 'src/StyleEditor.cpp', @@ -726,7 +723,9 @@ commonSourceFiles = files([ 'src/WaterTableWidget.cpp', 'src/widgets/Animator.cpp', 'src/widgets/SelectionControl.cpp', + 'src/widgets/SmartAmounts.cpp', 'src/widgets/SmartDigitWidget.cpp', + 'src/widgets/SmartField.cpp', 'src/widgets/SmartLabel.cpp', 'src/widgets/SmartLineEdit.cpp', 'src/widgets/ToggleSwitch.cpp', diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f0f875ae..03982716 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -150,8 +150,6 @@ set(filesToCompile_cpp ${repoDir}/src/RefractoDialog.cpp ${repoDir}/src/ScaleRecipeTool.cpp ${repoDir}/src/SimpleUndoableUpdate.cpp - ${repoDir}/src/SmartAmounts.cpp - ${repoDir}/src/SmartField.cpp ${repoDir}/src/StrikeWaterDialog.cpp ${repoDir}/src/StyleButton.cpp ${repoDir}/src/StyleEditor.cpp @@ -186,7 +184,9 @@ set(filesToCompile_cpp ${repoDir}/src/WaterTableWidget.cpp ${repoDir}/src/widgets/Animator.cpp ${repoDir}/src/widgets/SelectionControl.cpp + ${repoDir}/src/widgets/SmartAmounts.cpp ${repoDir}/src/widgets/SmartDigitWidget.cpp + ${repoDir}/src/widgets/SmartField.cpp ${repoDir}/src/widgets/SmartLabel.cpp ${repoDir}/src/widgets/SmartLineEdit.cpp ${repoDir}/src/widgets/ToggleSwitch.cpp diff --git a/src/EquipmentEditor.cpp b/src/EquipmentEditor.cpp index 2e8c0a37..de3b633b 100644 --- a/src/EquipmentEditor.cpp +++ b/src/EquipmentEditor.cpp @@ -203,7 +203,7 @@ void EquipmentEditor::save() { inform = inform + QString("
  • %1
  • ").arg(tr("batch size")); } - if ( qFuzzyCompare(lineEdit_hopUtilization->getValueAs(), 0.0) ) { + if ( qFuzzyCompare(lineEdit_hopUtilization->getNonOptValueAs(), 0.0) ) { problems = true; inform = inform + QString("
  • %1
  • ").arg(tr("hop utilization")); } @@ -227,7 +227,7 @@ void EquipmentEditor::save() { this->obsEquip->setBatchSize_l (lineEdit_batchSize ->toCanonical().quantity() ); this->obsEquip->setTunVolume_l (lineEdit_tunVolume ->toCanonical().quantity() ); this->obsEquip->setTunWeight_kg (lineEdit_tunWeight ->toCanonical().quantity() ); - this->obsEquip->setTunSpecificHeat_calGC(lineEdit_tunSpecificHeat->getValueAs() ); // TODO Convert this to real units! + this->obsEquip->setTunSpecificHeat_calGC(lineEdit_tunSpecificHeat->getNonOptValueAs() ); // TODO Convert this to real units! this->obsEquip->setBoilTime_min (lineEdit_boilTime ->toCanonical().quantity()); this->obsEquip->setEvapRate_lHr (lineEdit_evaporationRate->toCanonical().quantity() ); this->obsEquip->setTopUpKettle_l (lineEdit_topUpKettle ->toCanonical().quantity() ); @@ -236,7 +236,7 @@ void EquipmentEditor::save() { this->obsEquip->setLauterDeadspace_l (lineEdit_lauterDeadspace->toCanonical().quantity() ); this->obsEquip->setGrainAbsorption_LKg (ga_LKg ); this->obsEquip->setBoilingPoint_c (lineEdit_boilingPoint ->toCanonical().quantity() ); - this->obsEquip->setHopUtilization_pct (lineEdit_hopUtilization ->getValueAs() ); + this->obsEquip->setHopUtilization_pct (lineEdit_hopUtilization ->getNonOptValueAs() ); this->obsEquip->setNotes (textEdit_notes ->toPlainText()); this->obsEquip->setCalcBoilVolume (checkBox_calcBoilVolume ->checkState() == Qt::Checked); diff --git a/src/FermentableEditor.cpp b/src/FermentableEditor.cpp index 14cc033c..84e0fd74 100644 --- a/src/FermentableEditor.cpp +++ b/src/FermentableEditor.cpp @@ -87,19 +87,19 @@ void FermentableEditor::save() { Fermentable::typeStringMapping.stringToEnum(comboBox_fermentableType->currentData().toString()) ); - obsFerm->setYield_pct (lineEdit_yield ->getValueAs()); + obsFerm->setYield_pct (lineEdit_yield ->getNonOptValueAs()); obsFerm->setColor_srm (lineEdit_color ->toCanonical().quantity()); obsFerm->setAddAfterBoil (checkBox_addAfterBoil ->checkState() == Qt::Checked); obsFerm->setOrigin (lineEdit_origin ->text()); obsFerm->setSupplier (lineEdit_supplier ->text()); - obsFerm->setCoarseFineDiff_pct (lineEdit_coarseFineDiff->getValueAs()); - obsFerm->setMoisture_pct (lineEdit_moisture ->getValueAs()); + obsFerm->setCoarseFineDiff_pct (lineEdit_coarseFineDiff->getNonOptValueAs()); + obsFerm->setMoisture_pct (lineEdit_moisture ->getNonOptValueAs()); obsFerm->setDiastaticPower_lintner(lineEdit_diastaticPower->toCanonical().quantity()); - obsFerm->setProtein_pct (lineEdit_protein ->getValueAs()); - obsFerm->setMaxInBatch_pct (lineEdit_maxInBatch ->getValueAs()); + obsFerm->setProtein_pct (lineEdit_protein ->getNonOptValueAs()); + obsFerm->setMaxInBatch_pct (lineEdit_maxInBatch ->getNonOptValueAs()); obsFerm->setRecommendMash (checkBox_recommendMash ->checkState() == Qt::Checked); obsFerm->setIsMashed (checkBox_isMashed ->checkState() == Qt::Checked); - obsFerm->setIbuGalPerLb (lineEdit_ibuGalPerLb ->getValueAs()); // .:TBD:. No metric measure? + obsFerm->setIbuGalPerLb (lineEdit_ibuGalPerLb ->getNonOptValueAs()); // .:TBD:. No metric measure? obsFerm->setNotes (textEdit_notes ->toPlainText()); if (this->obsFerm->key() < 0) { diff --git a/src/HopEditor.cpp b/src/HopEditor.cpp index 4da06202..ce06734f 100644 --- a/src/HopEditor.cpp +++ b/src/HopEditor.cpp @@ -107,18 +107,18 @@ void HopEditor::save() { return; } - this->obsHop->setName (this->lineEdit_name ->text ()); - this->obsHop->setAlpha_pct (this->lineEdit_alpha ->getValueAs ()); - this->obsHop->setTime_min (this->lineEdit_time ->toCanonical().quantity()); - this->obsHop->setBeta_pct (this->lineEdit_beta ->getValueAs ()); - this->obsHop->setHsi_pct (this->lineEdit_HSI ->getValueAs ()); - this->obsHop->setOrigin (this->lineEdit_origin ->text ()); - this->obsHop->setHumulene_pct (this->lineEdit_humulene ->getValueAs ()); - this->obsHop->setCaryophyllene_pct(this->lineEdit_caryophyllene->getValueAs ()); - this->obsHop->setCohumulone_pct (this->lineEdit_cohumulone ->getValueAs ()); - this->obsHop->setMyrcene_pct (this->lineEdit_myrcene ->getValueAs ()); - this->obsHop->setSubstitutes (this->textEdit_substitutes ->toPlainText ()); - this->obsHop->setNotes (this->textEdit_notes ->toPlainText ()); + this->obsHop->setName (this->lineEdit_name ->text ()); + this->obsHop->setAlpha_pct (this->lineEdit_alpha ->getNonOptValueAs()); + this->obsHop->setTime_min (this->lineEdit_time ->toCanonical().quantity ()); + this->obsHop->setBeta_pct (this->lineEdit_beta ->getNonOptValueAs()); + this->obsHop->setHsi_pct (this->lineEdit_HSI ->getNonOptValueAs()); + this->obsHop->setOrigin (this->lineEdit_origin ->text ()); + this->obsHop->setHumulene_pct (this->lineEdit_humulene ->getNonOptValueAs()); + this->obsHop->setCaryophyllene_pct(this->lineEdit_caryophyllene->getNonOptValueAs()); + this->obsHop->setCohumulone_pct (this->lineEdit_cohumulone ->getNonOptValueAs()); + this->obsHop->setMyrcene_pct (this->lineEdit_myrcene ->getNonOptValueAs()); + this->obsHop->setSubstitutes (this->textEdit_substitutes ->toPlainText ()); + this->obsHop->setNotes (this->textEdit_notes ->toPlainText ()); // // It's a coding error if we don't recognise the values in our own combo boxes, so it's OK that we'd get a @@ -129,19 +129,19 @@ void HopEditor::save() { this->obsHop->setUse (Hop::useStringMapping.stringToEnum (comboBox_hopUse->currentData().toString())); // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - this->obsHop->setProducer (this->lineEdit_producer ->text ()); - this->obsHop->setProduct_id (this->lineEdit_product_id ->text ()); - this->obsHop->setYear (this->lineEdit_year ->getValueAs ()); - this->obsHop->setTotal_oil_ml_per_100g(this->lineEdit_total_oil_ml_per_100g->getValueAs ()); - this->obsHop->setFarnesene_pct (this->lineEdit_farnesene ->getValueAs ()); - this->obsHop->setGeraniol_pct (this->lineEdit_geraniol ->getValueAs ()); - this->obsHop->setB_pinene_pct (this->lineEdit_b_pinene ->getValueAs ()); - this->obsHop->setLinalool_pct (this->lineEdit_linalool ->getValueAs ()); - this->obsHop->setLimonene_pct (this->lineEdit_limonene ->getValueAs ()); - this->obsHop->setNerol_pct (this->lineEdit_nerol ->getValueAs ()); - this->obsHop->setPinene_pct (this->lineEdit_pinene ->getValueAs ()); - this->obsHop->setPolyphenols_pct (this->lineEdit_polyphenols ->getValueAs ()); - this->obsHop->setXanthohumol_pct (this->lineEdit_xanthohumol ->getValueAs ()); + this->obsHop->setProducer (this->lineEdit_producer ->text ()); + this->obsHop->setProduct_id (this->lineEdit_product_id ->text ()); + this->obsHop->setYear (this->lineEdit_year ->getOptValueAs ()); + this->obsHop->setTotal_oil_ml_per_100g(this->lineEdit_total_oil_ml_per_100g->getOptValueAs()); + this->obsHop->setFarnesene_pct (this->lineEdit_farnesene ->getOptValueAs()); + this->obsHop->setGeraniol_pct (this->lineEdit_geraniol ->getOptValueAs()); + this->obsHop->setB_pinene_pct (this->lineEdit_b_pinene ->getOptValueAs()); + this->obsHop->setLinalool_pct (this->lineEdit_linalool ->getOptValueAs()); + this->obsHop->setLimonene_pct (this->lineEdit_limonene ->getOptValueAs()); + this->obsHop->setNerol_pct (this->lineEdit_nerol ->getOptValueAs()); + this->obsHop->setPinene_pct (this->lineEdit_pinene ->getOptValueAs()); + this->obsHop->setPolyphenols_pct (this->lineEdit_polyphenols ->getOptValueAs()); + this->obsHop->setXanthohumol_pct (this->lineEdit_xanthohumol ->getOptValueAs()); if (this->obsHop->key() < 0) { ObjectStoreWrapper::insert(*this->obsHop); diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 3b412916..590482ab 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -1708,14 +1708,14 @@ void MainWindow::updateRecipeBoilTime() { } void MainWindow::updateRecipeEfficiency() { - qDebug() << Q_FUNC_INFO << lineEdit_efficiency->getValueAs(); + qDebug() << Q_FUNC_INFO << lineEdit_efficiency->getNonOptValueAs(); if (!this->recipeObs) { return; } this->doOrRedoUpdate(*this->recipeObs, PropertyNames::Recipe::efficiency_pct, - lineEdit_efficiency->getValueAs(), + lineEdit_efficiency->getNonOptValueAs(), tr("Change Recipe Efficiency")); return; } diff --git a/src/StyleEditor.cpp b/src/StyleEditor.cpp index ef948841..dccd8b32 100644 --- a/src/StyleEditor.cpp +++ b/src/StyleEditor.cpp @@ -130,14 +130,14 @@ void StyleEditor::save() { this->obsStyle->setOgMax (lineEdit_ogMax ->toCanonical().quantity() ); this->obsStyle->setFgMin (lineEdit_fgMin ->toCanonical().quantity() ); this->obsStyle->setFgMax (lineEdit_fgMax ->toCanonical().quantity() ); - this->obsStyle->setIbuMin (lineEdit_ibuMin ->getValueAs() ); - this->obsStyle->setIbuMax (lineEdit_ibuMax ->getValueAs() ); + this->obsStyle->setIbuMin (lineEdit_ibuMin ->getNonOptValueAs() ); + this->obsStyle->setIbuMax (lineEdit_ibuMax ->getNonOptValueAs() ); this->obsStyle->setColorMin_srm (lineEdit_colorMin ->toCanonical().quantity() ); this->obsStyle->setColorMax_srm (lineEdit_colorMax ->toCanonical().quantity() ); this->obsStyle->setCarbMin_vol (lineEdit_carbMin ->toCanonical().quantity() ); this->obsStyle->setCarbMax_vol (lineEdit_carbMax ->toCanonical().quantity() ); - this->obsStyle->setAbvMin_pct (lineEdit_abvMin ->getValueAs() ); - this->obsStyle->setAbvMax_pct (lineEdit_abvMax ->getValueAs() ); + this->obsStyle->setAbvMin_pct (lineEdit_abvMin ->getNonOptValueAs() ); + this->obsStyle->setAbvMax_pct (lineEdit_abvMax ->getNonOptValueAs() ); this->obsStyle->setProfile (textEdit_profile ->toPlainText() ); this->obsStyle->setIngredients (textEdit_ingredients ->toPlainText() ); this->obsStyle->setExamples (textEdit_examples ->toPlainText() ); diff --git a/src/YeastEditor.cpp b/src/YeastEditor.cpp index 395287eb..826adcf2 100644 --- a/src/YeastEditor.cpp +++ b/src/YeastEditor.cpp @@ -84,9 +84,9 @@ void YeastEditor::save() { this->obsYeast->setMinTemperature_c(lineEdit_minTemperature->toCanonical().quantity() ); this->obsYeast->setMaxTemperature_c(lineEdit_maxTemperature->toCanonical().quantity() ); this->obsYeast->setFlocculation (static_cast(comboBox_flocculation->currentIndex())); - this->obsYeast->setAttenuation_pct (lineEdit_attenuation ->getValueAs() ); - this->obsYeast->setTimesCultured (lineEdit_timesCultured ->getValueAs() ); - this->obsYeast->setMaxReuse (lineEdit_maxReuse ->getValueAs() ); + this->obsYeast->setAttenuation_pct (lineEdit_attenuation ->getNonOptValueAs() ); + this->obsYeast->setTimesCultured (lineEdit_timesCultured ->getNonOptValueAs() ); + this->obsYeast->setMaxReuse (lineEdit_maxReuse ->getNonOptValueAs() ); this->obsYeast->setAddToSecondary (checkBox_addToSecondary->checkState() == Qt::Checked ); this->obsYeast->setBestFor (textEdit_bestFor ->toPlainText() ); this->obsYeast->setNotes (textEdit_notes ->toPlainText() ); diff --git a/src/measurement/Measurement.h b/src/measurement/Measurement.h index 8386efe1..ed216116 100644 --- a/src/measurement/Measurement.h +++ b/src/measurement/Measurement.h @@ -36,7 +36,10 @@ namespace Measurement { /** * \brief Use this when you want to get the text as a number (and ignore any units or other trailling letters or - * symbols) + * symbols). Valid specialisations are \c int, \c unsigned \c int, and \c double. + * + * \param ok If set, used to return \c true if parsing of raw text went OK and \c false otherwise (in which case, + * function return value will be 0). */ template T extractRawFromString(QString const & input, bool * ok = nullptr); diff --git a/src/model/Hop.cpp b/src/model/Hop.cpp index 79f7f694..5a6bf0cc 100644 --- a/src/model/Hop.cpp +++ b/src/model/Hop.cpp @@ -291,21 +291,21 @@ Hop::Hop(Hop const & other) : Hop::~Hop() = default; //============================================= "GETTER" MEMBER FUNCTIONS ============================================== -Hop::Use Hop::use() const { return this->m_use; } -QString Hop::notes() const { return this->m_notes; } -Hop::Type Hop::type() const { return this->m_type; } -Hop::Form Hop::form() const { return this->m_form; } -QString Hop::origin() const { return this->m_origin; } -QString Hop::substitutes() const { return this->m_substitutes; } -double Hop::alpha_pct() const { return this->m_alpha_pct; } -double Hop::amount_kg() const { return this->m_amount_kg; } -double Hop::time_min() const { return this->m_time_min; } -double Hop::beta_pct() const { return this->m_beta_pct; } -double Hop::hsi_pct() const { return this->m_hsi_pct; } -double Hop::humulene_pct() const { return this->m_humulene_pct; } -double Hop::caryophyllene_pct() const { return this->m_caryophyllene_pct; } -double Hop::cohumulone_pct() const { return this->m_cohumulone_pct; } -double Hop::myrcene_pct() const { return this->m_myrcene_pct; } +Hop::Use Hop::use() const { return this->m_use; } +QString Hop::notes() const { return this->m_notes; } +Hop::Type Hop::type() const { return this->m_type; } +Hop::Form Hop::form() const { return this->m_form; } +QString Hop::origin() const { return this->m_origin; } +QString Hop::substitutes() const { return this->m_substitutes; } +double Hop::alpha_pct() const { return this->m_alpha_pct; } +double Hop::amount_kg() const { return this->m_amount_kg; } +double Hop::time_min() const { return this->m_time_min; } +double Hop::beta_pct() const { return this->m_beta_pct; } +double Hop::hsi_pct() const { return this->m_hsi_pct; } +double Hop::humulene_pct() const { return this->m_humulene_pct; } +double Hop::caryophyllene_pct() const { return this->m_caryophyllene_pct; } +double Hop::cohumulone_pct() const { return this->m_cohumulone_pct; } +double Hop::myrcene_pct() const { return this->m_myrcene_pct; } // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ QString Hop::producer() const { return this->m_producer; } QString Hop::product_id() const { return this->m_product_id; } diff --git a/src/tableModels/BtTableModel.cpp b/src/tableModels/BtTableModel.cpp index b0db2bb3..012600bd 100644 --- a/src/tableModels/BtTableModel.cpp +++ b/src/tableModels/BtTableModel.cpp @@ -25,7 +25,7 @@ #include "measurement/Unit.h" #include "measurement/UnitSystem.h" #include "utils/OptionalHelpers.h" -#include "SmartAmounts.h" +#include "widgets/SmartAmounts.h" #include "widgets/UnitAndScalePopUpMenu.h" BtTableModelRecipeObserver::BtTableModelRecipeObserver(QTableView * parent, diff --git a/src/utils/TypeLookup.h b/src/utils/TypeLookup.h index e008e7ab..052def71 100644 --- a/src/utils/TypeLookup.h +++ b/src/utils/TypeLookup.h @@ -53,13 +53,17 @@ * (at the simple end of things!) is somewhat inspired by the examples at: * https://www.boost.org/doc/libs/1_81_0/libs/type_traits/doc/html/boost_typetraits/background.html * - * Normally we shouldn't need to use these templates directly outside of the \c TypeLookup class because the + * Mostly we shouldn't need to use these templates directly outside of the \c TypeLookup class because the * \c PROPERTY_TYPE_LOOKUP_ENTRY macro below takes care of everything for constructor calls where you would - * otherwise need them. + * otherwise need them. However, they are sometimes useful for, eg, declaring templated functions where we need + * different versions for optional and non-optional, such as \c SmartField::setAmount. */ template struct is_optional : public std::false_type{}; template struct is_optional< std::optional > : public std::true_type{}; +template struct is_not_optional : public std::true_type{}; +template struct is_not_optional< std::optional > : public std::false_type{}; + template struct is_optional_enum : public std::false_type{}; template struct is_optional_enum< std::optional > : public std::is_enum{}; @@ -86,7 +90,9 @@ template concept IsOptionalOther = !is_optional_enum::value && i */ struct TypeInfo { /** - * \brief \c std::type_index is essentially a wrapper around pointer to \c std::type_info. It is guaranteed unique + * \brief This is the type ID of the \b underlying type, eg should be the same for \c int and \c std::optional. + * + * \c std::type_index is essentially a wrapper around pointer to \c std::type_info. It is guaranteed unique * for each different type and guaranteed to compare equal for two properties of the same type. (This is * better than using raw pointers as they are not guaranteed to be identical for two properties of the same * type.) @@ -139,12 +145,14 @@ struct TypeInfo { /** * \brief Factory functions to construct a \c TypeInfo for a given type. + * + * Note that if \c T is \c std::optional then U can be extracted by \c typename \c T::value_type. */ template const static TypeInfo construct(std::optional fieldType); // No general case, only specialisations template const static TypeInfo construct(std::optional fieldType = std::nullopt) { return TypeInfo{typeid(T), Classification::RequiredEnum , fieldType}; } template const static TypeInfo construct(std::optional fieldType = std::nullopt) { return TypeInfo{typeid(T), Classification::RequiredOther, fieldType}; } - template const static TypeInfo construct(std::optional fieldType = std::nullopt) { return TypeInfo{typeid(T), Classification::OptionalEnum , fieldType}; } - template const static TypeInfo construct(std::optional fieldType = std::nullopt) { return TypeInfo{typeid(T), Classification::OptionalOther, fieldType}; } + template const static TypeInfo construct(std::optional fieldType = std::nullopt) { return TypeInfo{typeid(typename T::value_type), Classification::OptionalEnum , fieldType}; } + template const static TypeInfo construct(std::optional fieldType = std::nullopt) { return TypeInfo{typeid(typename T::value_type), Classification::OptionalOther, fieldType}; } }; @@ -233,4 +241,17 @@ S & operator<<(S & stream, TypeInfo const & typeInfo) { return stream; } +/** + * \brief Convenience function for logging + */ +template +S & operator<<(S & stream, TypeInfo const * typeInfo) { + if (!typeInfo) { + stream << "nullptr"; + } else { + stream << *typeInfo; + } + return stream; +} + #endif diff --git a/src/SmartAmounts.cpp b/src/widgets/SmartAmounts.cpp similarity index 98% rename from src/SmartAmounts.cpp rename to src/widgets/SmartAmounts.cpp index 235ea0c2..3b7d0aae 100644 --- a/src/SmartAmounts.cpp +++ b/src/widgets/SmartAmounts.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * SmartAmounts.cpp is part of Brewken, and is copyright the following authors 2023: + * widgets/SmartAmounts.cpp is part of Brewken, and is copyright the following authors 2023: * • Matt Young * * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#include "SmartAmounts.h" +#include "widgets/SmartAmounts.h" #include @@ -21,7 +21,7 @@ #include "PersistentSettings.h" #include "utils/TypeLookup.h" #include "widgets/SmartLabel.h" -#include "SmartField.h" +#include "widgets/SmartField.h" template<> void SmartAmounts::Init(char const * const editorName, char const * const labelName, diff --git a/src/SmartAmounts.h b/src/widgets/SmartAmounts.h similarity index 99% rename from src/SmartAmounts.h rename to src/widgets/SmartAmounts.h index 7ab7900d..52c39af6 100644 --- a/src/SmartAmounts.h +++ b/src/widgets/SmartAmounts.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * SmartAmounts.h is part of Brewken, and is copyright the following authors 2023: + * widgets/SmartAmounts.h is part of Brewken, and is copyright the following authors 2023: * • Matt Young * * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/src/widgets/SmartDigitWidget.h b/src/widgets/SmartDigitWidget.h index 43f8a911..a77a0769 100644 --- a/src/widgets/SmartDigitWidget.h +++ b/src/widgets/SmartDigitWidget.h @@ -29,7 +29,7 @@ #include "measurement/PhysicalQuantity.h" #include "measurement/Unit.h" #include "measurement/UnitSystem.h" -#include "SmartField.h" +#include "widgets/SmartField.h" /*! * \class SmartDigitWidget diff --git a/src/SmartField.cpp b/src/widgets/SmartField.cpp similarity index 84% rename from src/SmartField.cpp rename to src/widgets/SmartField.cpp index 85a976e4..4a326ece 100644 --- a/src/SmartField.cpp +++ b/src/widgets/SmartField.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * SmartField.cpp is part of Brewken, and is copyright the following authors 2009-2023: + * widgets/SmartField.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Mark de Wever * • Mattias Måhl @@ -20,7 +20,7 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#include "SmartField.h" +#include "widgets/SmartField.h" #include @@ -35,6 +35,21 @@ #include "utils/TypeLookup.h" #include "widgets/SmartLabel.h" +namespace { + /** + * \return \c true if \c input is empty or blank (ie contains only whitespace), \c false otherwise + */ + [[nodiscard]] bool isEmptyOrBlank(QString const & input) { + if (input.isEmpty()) { + return true; + } + if (input.trimmed().isEmpty()) { + return true; + } + return false; + } +} + // This private implementation class holds all private non-virtual members of SmartField class SmartField::impl { public: @@ -370,15 +385,24 @@ SmartAmounts::ScaleInfo SmartField::getScaleInfo() const { ConvertToPhysicalQuantities(*this->pimpl->m_typeInfo->fieldType)); } -template -void SmartField::setAmount(std::optional amount) { +// Note that, because partial specialisation of _functions_ is not allowed, we actually have two overloads of setAmount +// This shouldn't make any difference to callers. +// +// It looks a bit funky disabling this specialisation for a T that is optional, but the point is that we don't want the +// compiler to ever create a std::optional> type. (Eg, we don't want to write +// `setAmount>(std::nullopt)` when we mean `setAmount(std::optional{std::nullopt})` (or actually +// `setAmount()`). +// +template::value> > void SmartField::setAmount(std::optional amount) { Q_ASSERT(this->pimpl->m_initialised); if (this->pimpl->m_typeInfo->typeIndex != typeid(T)) { + // This is a coding error qCritical() << Q_FUNC_INFO << this->pimpl->m_fieldFqName << ": Trying to set wrong type; m_typeInfo=" << - this->pimpl->m_typeInfo; - } + this->pimpl->m_typeInfo << ", typeid(T)=" << typeid(T).name(); + Q_ASSERT(false); + } if (!amount) { this->setRawText(""); @@ -389,26 +413,16 @@ void SmartField::setAmount(std::optional amount) { return; } -// -// Instantiate the above template function 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.) -// -template void SmartField::setAmount(std::optional amount); -template void SmartField::setAmount(std::optional amount); -template void SmartField::setAmount(std::optional amount); - -template void SmartField::setAmount(T amount) { - // Only need next bit for debugging! -// if (!this->pimpl->m_initialised) { -// qCritical().noquote() << Q_FUNC_INFO << this->pimpl->m_fieldFqName << "Stack trace:" << Logging::getStackTrace(); -// } +template::value> > void SmartField::setAmount(T amount) { Q_ASSERT(this->pimpl->m_initialised); qDebug() << Q_FUNC_INFO << this->pimpl->m_fieldFqName << "amount =" << amount; if (this->pimpl->m_typeInfo->typeIndex != typeid(T)) { + // This is a coding error qCritical() << - Q_FUNC_INFO << this->pimpl->m_fieldFqName << ": Trying to set wrong type; m_typeInfo=" << this->pimpl->m_typeInfo; + Q_FUNC_INFO << this->pimpl->m_fieldFqName << ": Trying to set wrong type; m_typeInfo=" << + this->pimpl->m_typeInfo << ", typeid(T)=" << typeid(T).name(); + Q_ASSERT(false); } if (std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)) { @@ -440,24 +454,71 @@ template void SmartField::setAmount(T amount) { return; } -template void SmartField::setAmount(int amount); -template void SmartField::setAmount(unsigned int amount); -template void SmartField::setAmount(double amount); +// 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.) +template void SmartField::setAmount(std::optional amount); +template void SmartField::setAmount(std::optional amount); +template void SmartField::setAmount(std::optional amount); +template void SmartField::setAmount(int amount); +template void SmartField::setAmount(unsigned int amount); +template void SmartField::setAmount(double amount); -template T SmartField::getValueAs() const { - qDebug() << - Q_FUNC_INFO << this->pimpl->m_fieldFqName << ": Converting" << this->getRawText() << "to" << - Measurement::extractRawFromString(this->getRawText()); - return Measurement::extractRawFromString(this->getRawText()); +// We can't do the same trick on get-value-as as we do for set-amount because we can't overload base on return type, +// hence two different function names. +template T SmartField::getNonOptValueAs(bool * const ok) const { + Q_ASSERT(this->pimpl->m_initialised); + + QString const rawText = this->getRawText(); + qDebug() << Q_FUNC_INFO << this->pimpl->m_fieldFqName << ": Converting" << rawText; + + // It's a coding error to call this for ann optional value. We put the assert after the log statement to help + // with debugging! + Q_ASSERT(!this->pimpl->m_typeInfo->isOptional()); + + // Note that Measurement::extractRawFromString returns 0 if it can't parse the text + return Measurement::extractRawFromString(rawText, ok); } -// // Instantiate the above template function 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.) -// -template int SmartField::getValueAs() const; -template unsigned int SmartField::getValueAs() const; -template double SmartField::getValueAs() const; +template int SmartField::getNonOptValueAs(bool * const ok) const; +template unsigned int SmartField::getNonOptValueAs(bool * const ok) const; +template double SmartField::getNonOptValueAs(bool * const ok) const; + +template std::optional SmartField::getOptValueAs(bool * const ok) const { + + Q_ASSERT(this->pimpl->m_initialised); + + QString const rawText = this->getRawText(); + qDebug() << Q_FUNC_INFO << this->pimpl->m_fieldFqName << ": Converting" << rawText; + + // It's a coding error to call this for a non optional value. We put the assert after the log statement to help + // with debugging! + Q_ASSERT(this->pimpl->m_typeInfo->isOptional()); + + // Optional values are allowed to be blank + if (isEmptyOrBlank(rawText)) { + if (ok) { + *ok = true; + } + return std::optional{std::nullopt}; + } + + bool parseOk = false; + T amount = Measurement::extractRawFromString(rawText, &parseOk); + if (ok) { + *ok = parseOk; + } + // If we couldn't parse something, return null + if (!parseOk) { + return std::optional{std::nullopt}; + } + + return std::make_optional(amount); +} +// Instantiate the above template function for the types that are going to use it +template std::optional SmartField::getOptValueAs(bool * const ok) const; +template std::optional SmartField::getOptValueAs(bool * const ok) const; +template std::optional SmartField::getOptValueAs(bool * const ok) const; Measurement::PhysicalQuantity SmartField::getPhysicalQuantity() const { @@ -483,52 +544,6 @@ void SmartField::selectPhysicalQuantity(Measurement::PhysicalQuantity const phys return; } -///void SmartField::setForcedSystemOfMeasurement(std::optional systemOfMeasurement) { -/// qDebug() << -/// Q_FUNC_INFO << "Measurement system" << systemOfMeasurement << "for" << this->pimpl->m_configSection << ">" << -/// this->pimpl->m_editField; -/// Measurement::setForcedSystemOfMeasurementForField(this->pimpl->m_editField, -/// this->pimpl->m_configSection, -/// systemOfMeasurement); -/// return; -///} -/// -///std::optional SmartField::getForcedSystemOfMeasurement() const { -/// return Measurement::getForcedSystemOfMeasurementForField(this->pimpl->m_editField, this->pimpl->m_configSection); -///} -/// -///void SmartField::setForcedRelativeScale(std::optional relativeScale) { -/// qDebug() << -/// Q_FUNC_INFO << "Scale" << relativeScale << "for" << this->pimpl->m_configSection << ">" << -/// this->pimpl->m_editField; -/// Measurement::setForcedRelativeScaleForField(this->pimpl->m_editField, this->pimpl->m_configSection, relativeScale); -/// return; -///} -/// -///std::optional SmartField::getForcedRelativeScale() const { -/// return Measurement::getForcedRelativeScaleForField(this->pimpl->m_editField, this->pimpl->m_configSection); -///} -/// -///SmartAmounts::ScaleInfo SmartField::getPreviousScaleInfo() const { -/// qDebug() << -/// Q_FUNC_INFO << "Edit Field / Property Name:" << this->pimpl->m_editField << ", Config Section" << -/// this->pimpl->m_configSection; -/// -/// -/// SmartAmounts::ScaleInfo previousScaleInfo{ -/// Measurement::getSystemOfMeasurementForField(this->pimpl->m_editField, this->pimpl->m_configSection, this->pimpl->m_currentPhysicalQuantity), -/// Measurement::getForcedRelativeScaleForField(this->pimpl->m_editField, this->pimpl->m_configSection) -/// }; -/// return previousScaleInfo; -///} - -///Measurement::Amount SmartField::rawToCanonical(QString const & rawValue) const { -/// return Measurement::qStringToSI(rawValue, -/// this->pimpl->m_currentPhysicalQuantity, -/// this->getForcedSystemOfMeasurement(), -/// this->getForcedRelativeScale()); -///} - QString SmartField::displayAmount(double amount) const { // It's a coding error to call this for NonPhysicalQuantity Q_ASSERT(!std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)); @@ -582,27 +597,25 @@ void SmartField::correctEnteredText() { NonPhysicalQuantity const nonPhysicalQuantity = std::get(*this->pimpl->m_typeInfo->fieldType); if (nonPhysicalQuantity != NonPhysicalQuantity::String) { + QString const rawText = this->getRawText(); + + auto const type = this->pimpl->m_typeInfo->typeIndex; + bool const optional = this->pimpl->m_typeInfo->isOptional(); bool ok = false; - if (this->pimpl->m_typeInfo->typeIndex == typeid(double)) { - double amount = Measurement::extractRawFromString(this->getRawText(), &ok); - this->setAmount(amount); - } else if (this->pimpl->m_typeInfo->typeIndex == typeid(int)) { - int amount = Measurement::extractRawFromString(this->getRawText(), &ok); - this->setAmount(amount); - } else if (this->pimpl->m_typeInfo->typeIndex == typeid(unsigned int)) { - unsigned int amount = Measurement::extractRawFromString(this->getRawText(), &ok); - this->setAmount(amount); - } else { + if (type == typeid(double )) { if (optional) { this->setAmount(this->getOptValueAs(&ok)); } else { this->setAmount(this->getNonOptValueAs(&ok)); } } else + if (type == typeid(int )) { if (optional) { this->setAmount(this->getOptValueAs(&ok)); } else { this->setAmount(this->getNonOptValueAs(&ok)); } } else + if (type == typeid(unsigned int)) { if (optional) { this->setAmount(this->getOptValueAs(&ok)); } else { this->setAmount(this->getNonOptValueAs(&ok)); } } else { // It's a coding error if we get here qCritical() << Q_FUNC_INFO << this->getFqFieldName() << ": Don't know how to parse" << this->pimpl->m_typeInfo; Q_ASSERT(false); } + if (!ok) { qWarning() << - Q_FUNC_INFO << this->getFqFieldName() << ": Unable to extract number from" << this->getRawText() << - "for" << this->pimpl->m_typeInfo; - this->setAmount(0); + Q_FUNC_INFO << this->getFqFieldName() << ": Unable to extract number from" << rawText << "for" << + this->pimpl->m_typeInfo; + // setAmount will already have been called with 0 or std::nullopt as appropriate } } diff --git a/src/SmartField.h b/src/widgets/SmartField.h similarity index 89% rename from src/SmartField.h rename to src/widgets/SmartField.h index 6da062ba..b7eddd1e 100644 --- a/src/SmartField.h +++ b/src/widgets/SmartField.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * SmartField.h is part of Brewken, and is copyright the following authors 2009-2023: + * widgets/SmartField.h is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Mark de Wever * • Matt Young @@ -33,7 +33,8 @@ #include "measurement/Unit.h" #include "measurement/UnitSystem.h" #include "utils/BtStringConst.h" -#include "SmartAmounts.h" +#include "utils/TypeLookup.h" +#include "widgets/SmartAmounts.h" class QWidget; @@ -85,7 +86,7 @@ class SmartField { * above), it also ensures, if necessary, that the \c changedSystemOfMeasurementOrScale signal from the * \c SmartLabel buddy is connected to the \c lineChanged slot of this \c SmartField. * - * Note, in reality, you actually use the \c SMART_FIELD_INIT macro (see \c SmartAmounts.h). + * Note, in reality, you actually use the \c SMART_FIELD_INIT macro (see \c widgets/SmartAmounts.h). * * \param editorName Name of the owning editor (eg "FermentableEditor"). Together with \c fieldName, should * uniquely identify this field. @@ -199,12 +200,18 @@ class SmartField { Measurement::Amount toCanonical() const; /** - * \brief Set the amount for a numeric field + * \brief Version of \c setAmount, for an optional amount. * - * \param amount is the amount to display, but the field should be blank if this is \b std::nullopt + * See comments in the .cpp file for why we have the enable_if here with is_not_optional. It is intentional! */ - template void setAmount(std::optional amount); - template void setAmount(T amount); + template::value> > void setAmount(std::optional amount); + + /** + * \brief Set the amount for a non-optional numeric field + * + * \param amount is the amount to display, which may be optional + */ + template::value> > void setAmount(T amount); void setForcedSystemOfMeasurement(std::optional systemOfMeasurement); void setForcedRelativeScale(std::optional relativeScale); @@ -219,9 +226,26 @@ class SmartField { /** * \brief Use this when you want to get the text as a number (and ignore any units or other trailling letters or - * symbols) + * symbols). + * + * This version is for non-optional (aka required) values. + * + * Valid instantiations are \c int, \c unsigned \c int, \c double + * + * \param ok If set, used to return \c true if parsing of raw text went OK and \c false otherwise (in which case, + * function return value will be 0). + */ + template T getNonOptValueAs(bool * const ok = nullptr) const; + + /** + * \brief As \c getNonOptValueAs but for std::optional values + * + * Valid instantiations are \c int, \c unsigned \c int, \c double + * + * \param ok If set, used to return \c true if parsing of raw text went OK and \c false otherwise (in which case, + * function return value will be \c std::nullopt). */ - template T getValueAs() const; + template std::optional getOptValueAs(bool * const ok = nullptr) const; /** * \brief Returns what type of field this is - except that, if it is \c Mixed2PhysicalQuantities, will one of the two @@ -239,14 +263,6 @@ class SmartField { */ void selectPhysicalQuantity(Measurement::PhysicalQuantity const physicalQuantity); - /** - * \brief Returns the field converted to canonical units for the relevant \c Measurement::PhysicalQuantity - * - * \param rawValue field text to process - * \return - */ -/// Measurement::Amount rawToCanonical(QString const & rawValue) const; - /** * \brief Use this when you want to do something with the returned QString * diff --git a/src/widgets/SmartLabel.cpp b/src/widgets/SmartLabel.cpp index 6b20cadd..d8199b6f 100644 --- a/src/widgets/SmartLabel.cpp +++ b/src/widgets/SmartLabel.cpp @@ -30,7 +30,7 @@ #include "model/Recipe.h" #include "PersistentSettings.h" #include "utils/OptionalHelpers.h" -#include "SmartField.h" +#include "widgets/SmartField.h" #include "widgets/UnitAndScalePopUpMenu.h" // This private implementation class holds all private non-virtual members of SmartLabel @@ -107,7 +107,7 @@ SmartLabel::~SmartLabel() = default; void SmartLabel::init(char const * const editorName, char const * const labelName, char const * const labelFqName, - SmartField * smartField, + [[maybe_unused]] SmartField * smartField, TypeInfo const & typeInfo) { qDebug() << Q_FUNC_INFO << labelFqName << ":" << typeInfo; diff --git a/src/widgets/SmartLabel.h b/src/widgets/SmartLabel.h index a3211c5b..7c9f1430 100644 --- a/src/widgets/SmartLabel.h +++ b/src/widgets/SmartLabel.h @@ -31,8 +31,8 @@ #include "BtFieldType.h" #include "measurement/UnitSystem.h" -#include "SmartField.h" // For SmartAmounts::ScaleInfo -#include "SmartAmounts.h" +#include "widgets/SmartField.h" // For SmartAmounts::ScaleInfo +#include "widgets/SmartAmounts.h" class SmartField; diff --git a/src/widgets/SmartLineEdit.cpp b/src/widgets/SmartLineEdit.cpp index 0f51aaae..95f65007 100644 --- a/src/widgets/SmartLineEdit.cpp +++ b/src/widgets/SmartLineEdit.cpp @@ -29,7 +29,7 @@ #include #include "measurement/Measurement.h" -#include "SmartField.h" +#include "widgets/SmartField.h" #include "utils/OptionalHelpers.h" #include "utils/TypeLookup.h" #include "widgets/SmartLabel.h" @@ -148,14 +148,11 @@ void SmartLineEdit::doPostInitWork() { void SmartLineEdit::onLineChanged() { Q_ASSERT(this->isInitialised()); + qDebug() << Q_FUNC_INFO; - // .:TODO:. Finish work on optional - if ("" == this->getRawText() && this->getTypeInfo().isOptional()) { - this->pimpl->setNoAmount(); - } else if (std::holds_alternative(*this->getTypeInfo().fieldType)) { + if (std::holds_alternative(*this->getTypeInfo().fieldType)) { // The field is not measuring a physical quantity so there are no units or unit conversions to handle // However, for anything other than a string, we still want to parse out the numeric part of the input - qDebug() << Q_FUNC_INFO; this->correctEnteredText(); if (sender() == this) { @@ -164,7 +161,7 @@ void SmartLineEdit::onLineChanged() { return; } - // The field is measuring a physical quantity + // The field must be measuring a physical quantity, because we dealt with the other case above! Q_ASSERT(!std::holds_alternative(*this->getTypeInfo().fieldType)); auto const previousScaleInfo = this->getScaleInfo(); diff --git a/src/widgets/SmartLineEdit.h b/src/widgets/SmartLineEdit.h index b93b5a2d..743e715a 100644 --- a/src/widgets/SmartLineEdit.h +++ b/src/widgets/SmartLineEdit.h @@ -31,8 +31,8 @@ #include "BtFieldType.h" #include "utils/TypeLookup.h" -#include "SmartField.h" -#include "SmartAmounts.h" +#include "widgets/SmartField.h" +#include "widgets/SmartAmounts.h" class QLabel; class SmartLabel; From 9cff9082db6776efe28c27f6d4652b705069d8ed Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sun, 30 Apr 2023 16:36:38 +0200 Subject: [PATCH 26/42] Build fixes & tidy up --- src/model/Fermentable.h | 11 ++++-- src/utils/TypeLookup.h | 2 +- src/widgets/SmartField.cpp | 78 +------------------------------------- src/widgets/SmartField.h | 6 ++- 4 files changed, 15 insertions(+), 82 deletions(-) diff --git a/src/model/Fermentable.h b/src/model/Fermentable.h index 72102636..1de7572b 100644 --- a/src/model/Fermentable.h +++ b/src/model/Fermentable.h @@ -267,7 +267,7 @@ class Fermentable : public NamedEntityWithInventory { * degree of malt modification. A value above 35% is desired for simple single infusion mashing, undermodified * malt may require multiple step mashes or decoction. */ - Q_PROPERTY(std::optional kolbachIndex_pct READ kolbachIndex_pct WRITE setKolbachIndex_pct ) + Q_PROPERTY(std::optional kolbachIndex_pct READ kolbachIndex_pct WRITE setKolbachIndex_pct ) /** * \brief Amounts of a \c Fermentable can be measured by mass or by volume (depending usually on what it is) @@ -275,7 +275,7 @@ class Fermentable : public NamedEntityWithInventory { * .:TBD JSON:. Check what else we need to do to tie in to Mixed2PhysicalQuantities, plus look at how we force weight * for BeerXML. */ - Q_PROPERTY(MassOrVolumeAmt amountWithUnits READ amountWithUnits WRITE setAmountWithUnits ) + Q_PROPERTY(MassOrVolumeAmt amountWithUnits READ amountWithUnits WRITE setAmountWithUnits) /** * \brief Percentage of malt that is "glassy". For a malt, % "glassy" + % "half glassy" + % "mealy" = 100%. @@ -337,13 +337,13 @@ class Fermentable : public NamedEntityWithInventory { * the malt will crush. Any lot of malt that will crush reasonably well must have kernels that are at * least 90% adjacent sizes, regardless of the plumpness. */ - Q_PROPERTY(std::optional kernelSizePrpPlump_pct READ kernelSizePrpPlump_pct WRITE setKernelSizePrpPlump_pct ) + Q_PROPERTY(std::optional kernelSizePrpPlump_pct READ kernelSizePrpPlump_pct WRITE setKernelSizePrpPlump_pct) /** * \brief The Percentage of grain that is "tine", ie makes it through a thin mesh screen, typically 5/64 inch. * Values less than 3% are desired. (In BeerJSON this is called "thru".) */ - Q_PROPERTY(std::optional kernelSizePrpThin_pct READ kernelSizePrpThin_pct WRITE setKernelSizePrpThin_pct ) + Q_PROPERTY(std::optional kernelSizePrpThin_pct READ kernelSizePrpThin_pct WRITE setKernelSizePrpThin_pct) /** * \brief Friability is the relative ease of crumbling when a malt is milled. It is related to mealiness, and may be @@ -396,6 +396,9 @@ class Fermentable : public NamedEntityWithInventory { * Measurement of DMS-P is often performed by heating "congress wort" samples (see above) in closed vials, * sampling the headspace after incubation, and quantifying DMS using gas chromatography. */ +/// Q_PROPERTY(std::optional dmsP_ppm READ dmsP_ppm WRITE setDmsP_ppm ) + + // dmsP_XXXunitXXX //conc units //fan //conc units //fermentability_pct diff --git a/src/utils/TypeLookup.h b/src/utils/TypeLookup.h index 052def71..09745907 100644 --- a/src/utils/TypeLookup.h +++ b/src/utils/TypeLookup.h @@ -72,7 +72,7 @@ template struct is_optional_enum< std::optional > : public std:: // Older versions of GCC (eg as shipped with Ubuntu 20.04 LTS) have a sort of pre-release support for concepts so we // have to use non-standard syntax there // -#if defined(__GNUC__) && (__GNUC__ < 10) +#if defined(__linux__ ) && defined(__GNUC__) && (__GNUC__ < 10) template concept bool IsRequiredEnum = std::is_enum::value; template concept bool IsRequiredOther = !std::is_enum::value && !is_optional::value; template concept bool IsOptionalEnum = is_optional_enum::value; diff --git a/src/widgets/SmartField.cpp b/src/widgets/SmartField.cpp index 4a326ece..4a9a1606 100644 --- a/src/widgets/SmartField.cpp +++ b/src/widgets/SmartField.cpp @@ -387,13 +387,7 @@ SmartAmounts::ScaleInfo SmartField::getScaleInfo() const { // Note that, because partial specialisation of _functions_ is not allowed, we actually have two overloads of setAmount // This shouldn't make any difference to callers. -// -// It looks a bit funky disabling this specialisation for a T that is optional, but the point is that we don't want the -// compiler to ever create a std::optional> type. (Eg, we don't want to write -// `setAmount>(std::nullopt)` when we mean `setAmount(std::optional{std::nullopt})` (or actually -// `setAmount()`). -// -template::value> > void SmartField::setAmount(std::optional amount) { +template void SmartField::setAmount(std::optional amount) { Q_ASSERT(this->pimpl->m_initialised); if (this->pimpl->m_typeInfo->typeIndex != typeid(T)) { @@ -413,7 +407,7 @@ template::value> > vo return; } -template::value> > void SmartField::setAmount(T amount) { +template void SmartField::setAmount(T amount) { Q_ASSERT(this->pimpl->m_initialised); qDebug() << Q_FUNC_INFO << this->pimpl->m_fieldFqName << "amount =" << amount; @@ -621,71 +615,3 @@ void SmartField::correctEnteredText() { return; } - -///void SmartField::textOrUnitsChanged(SmartAmounts::ScaleInfo previousScaleInfo) { -/// // This is where it gets hard -/// // -/// // We may need to fix the text that the user entered, eg if this field is set to show US Customary volumes and user -/// // enters an amount in litres then we need to convert it to display in pints or quarts etc. -/// QString correctedText; -/// -/// QString rawValue = this->getWidgetRawText(); -/// qDebug() << Q_FUNC_INFO << "rawValue:" << rawValue; -/// -/// if (rawValue.isEmpty()) { -/// return; -/// } -/// -/// // The idea here is we need to first translate the field into a known -/// // amount (aka to SI) and then into the unit we want. -/// Measurement::Amount amountAsCanonical = this->convertToSI(previousScaleInfo); -/// -/// Measurement::PhysicalQuantity physicalQuantity = this->getPhysicalQuantity(); -/// int precision = 3; -/// if (physicalQuantity == Measurement::PhysicalQuantity::Color) { -/// precision = 0; -/// } -/// correctedText = this->displayAmount(amountAsCanonical.quantity(), precision); -/// qDebug() << -/// Q_FUNC_INFO << "Interpreted" << rawValue << "as" << amountAsCanonical << "and corrected to" << correctedText; -/// -/// this->setWidgetRawText(correctedText); -/// return; -///} - -///Measurement::Amount SmartField::convertToSI(SmartAmounts::ScaleInfo previousScaleInfo) { -/// QString rawValue = this->getWidgetRawText(); -/// qDebug() << -/// Q_FUNC_INFO << "rawValue:" << rawValue << ", old SystemOfMeasurement:" << -/// previousScaleInfo.oldSystemOfMeasurement << ", old ForcedScale: " << previousScaleInfo.oldForcedScale; -/// -/// Measurement::UnitSystem const & oldUnitSystem = -/// Measurement::UnitSystem::getInstance(previousScaleInfo.oldSystemOfMeasurement, this->pimpl->m_currentPhysicalQuantity); -/// -/// Measurement::Unit const * defaultUnit{ -/// previousScaleInfo.oldForcedScale ? oldUnitSystem.scaleUnit(*previousScaleInfo.oldForcedScale) : oldUnitSystem.unit() -/// }; -/// -/// // It's a coding error if defaultUnit is null, because it means previousScaleInfo.oldForcedScale was not valid for -/// // oldUnitSystem. However, we can recover. -/// if (!defaultUnit) { -/// qWarning() << Q_FUNC_INFO << "previousScaleInfo.oldForcedScale invalid?" << previousScaleInfo.oldForcedScale; -/// defaultUnit = oldUnitSystem.unit(); -/// } -/// -/// // -/// // Normally, we display units with the text. If the user just edits the number, then the units will still be there. -/// // Alternatively, if the user specifies different units in the text, we should try to honour those. Otherwise, if, -/// // no units are specified in the text, we need to go to defaults. Defaults are either what is "forced" for this -/// // specific field or, failing that, what is configured globally. -/// // -/// // Measurement::UnitSystem::qStringToSI will handle all the logic to deal with any units specified by the user in the -/// // string. (In theory, we just grab the units that the user has specified in the input text. In reality, it's not -/// // that easy as we sometimes need to disambiguate - eg between Imperial gallons and US customary ones. So, if we -/// // have old or current units then that helps with this - eg, if current units are US customary cups and user enters -/// // gallons, then we'll go with US customary gallons over Imperial ones.) -/// // -/// auto amount = oldUnitSystem.qstringToSI(rawValue, *defaultUnit); -/// qDebug() << Q_FUNC_INFO << "Converted to" << amount; -/// return amount; -///} diff --git a/src/widgets/SmartField.h b/src/widgets/SmartField.h index b7eddd1e..fc0ebf60 100644 --- a/src/widgets/SmartField.h +++ b/src/widgets/SmartField.h @@ -202,7 +202,11 @@ class SmartField { /** * \brief Version of \c setAmount, for an optional amount. * - * See comments in the .cpp file for why we have the enable_if here with is_not_optional. It is intentional! + * It looks a bit funky disabling this specialisation for a T that is optional, but the point is that we don't + * want the compiler to ever create a \c std::optional> type. (Eg, we don't want to write + * `\c setAmount>(\c std::nullopt)` when we mean + * `\c setAmount(\c std::optional{std::nullopt})`. + * */ template::value> > void setAmount(std::optional amount); From f4136d3ece624fcdfc0552a32bb78c3bb1881b47 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Tue, 2 May 2023 18:53:58 +0200 Subject: [PATCH 27/42] DB migration and mapping for new Fermentable columns --- CMakeLists.txt | 8 +- src/BrewNoteWidget.cpp | 72 ++-- src/BtTreeModel.cpp | 2 + src/MainWindow.cpp | 30 +- src/WaterDialog.cpp | 81 ++-- src/database/DatabaseSchemaHelper.cpp | 41 +- src/database/ObjectStoreTyped.cpp | 39 +- src/json/BeerJson.cpp | 12 +- src/measurement/Amount.h | 35 -- src/measurement/Unit.cpp | 4 + src/measurement/Unit.h | 6 + src/model/Fermentable.cpp | 519 +++++++++++++++----------- src/model/Fermentable.h | 375 +++++++++++-------- src/model/NamedEntity.cpp | 94 +++++ src/model/NamedEntity.h | 43 +++ src/model/NamedParameterBundle.cpp | 4 + src/model/NamedParameterBundle.h | 14 +- src/utils/OptionalHelpers.h | 45 +++ src/utils/TypeLookup.h | 16 + src/widgets/SmartDigitWidget.cpp | 157 +++----- src/widgets/SmartDigitWidget.h | 41 +- src/widgets/SmartField.cpp | 56 +-- src/widgets/SmartField.h | 17 +- src/widgets/SmartLineEdit.cpp | 2 - ui/waterDialog.ui | 14 +- 25 files changed, 1006 insertions(+), 721 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0908bca1..2eba0ccf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ #---------------------------------------------------------------------------------------------------------------------- -# CMakeLists.txt is part of Brewken, and is copyright the following authors 2009-2022: +# CMakeLists.txt is part of Brewken, and is copyright the following authors 2009-2023: # • Chris Pavetto # • Dan Cavanagh # • Daniel Moreno @@ -24,6 +24,12 @@ # . #---------------------------------------------------------------------------------------------------------------------- +# ⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ +# NB: Meson and the `bt` build tool Python script are now the primary way of building and packaging the software. You +# can also still CMake to compile the product and install it locally, but we no longer support using CMake to do +# packaging. +# ⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ + # # Creates a Makefile in the build directory, from where you can do builds and installs. # diff --git a/src/BrewNoteWidget.cpp b/src/BrewNoteWidget.cpp index 4cea650a..791d3b83 100644 --- a/src/BrewNoteWidget.cpp +++ b/src/BrewNoteWidget.cpp @@ -48,15 +48,15 @@ BrewNoteWidget::BrewNoteWidget(QWidget *parent) : QWidget(parent) { SMART_FIELD_INIT(BrewNoteWidget, label_postBoilVol, lineEdit_postBoilVol , BrewNote, PropertyNames::BrewNote::postBoilVolume_l); SMART_FIELD_INIT(BrewNoteWidget, label_volIntoBk , lineEdit_volIntoBk , BrewNote, PropertyNames::BrewNote::volumeIntoBK_l ); SMART_FIELD_INIT(BrewNoteWidget, label_volIntoFerm, lineEdit_volIntoFerm , BrewNote, PropertyNames::BrewNote::volumeIntoFerm_l); - SMART_FIELD_INIT(BrewNoteWidget, label_projectedOg, lcdnumber_projectedOG, BrewNote, PropertyNames::BrewNote::projOg ); - // SMART_FIELD_INIT(BrewNoteWidget, label_fermentDate , lineEdit_fermentDate , BrewNote, PropertyNames::BrewNote::fermentDate ); No specialisation for QDateTimeEdit - SMART_FIELD_INIT(BrewNoteWidget, label_effInfoBk , lcdnumber_effBK , BrewNote, PropertyNames::BrewNote::effIntoBK_pct ); - SMART_FIELD_INIT(BrewNoteWidget, label_brewHouseEff , lcdnumber_brewhouseEff, BrewNote, PropertyNames::BrewNote::brewhouseEff_pct); - SMART_FIELD_INIT(BrewNoteWidget, label_projectedAbv , lcdnumber_projABV , BrewNote, PropertyNames::BrewNote::projABV_pct ); - SMART_FIELD_INIT(BrewNoteWidget, label_Abv , lcdnumber_abv , BrewNote, PropertyNames::BrewNote::abv ); - SMART_FIELD_INIT(BrewNoteWidget, label_yeastProjAtten, lcdnumber_projAtten , BrewNote, PropertyNames::BrewNote::projAtten ); - SMART_FIELD_INIT(BrewNoteWidget, label_yeastAtten , lcdnumber_atten , BrewNote, PropertyNames::BrewNote::attenuation ); + SMART_FIELD_INIT(BrewNoteWidget, label_projectedOg , lcdnumber_projectedOG , BrewNote, PropertyNames::BrewNote::projOg ); + SMART_FIELD_INIT(BrewNoteWidget, label_effInfoBk , lcdnumber_effBK , BrewNote, PropertyNames::BrewNote::effIntoBK_pct , 2); + SMART_FIELD_INIT(BrewNoteWidget, label_brewHouseEff , lcdnumber_brewhouseEff, BrewNote, PropertyNames::BrewNote::brewhouseEff_pct, 2); + SMART_FIELD_INIT(BrewNoteWidget, label_projectedAbv , lcdnumber_projABV , BrewNote, PropertyNames::BrewNote::projABV_pct , 2); + SMART_FIELD_INIT(BrewNoteWidget, label_Abv , lcdnumber_abv , BrewNote, PropertyNames::BrewNote::abv , 2); + SMART_FIELD_INIT(BrewNoteWidget, label_yeastProjAtten, lcdnumber_projAtten , BrewNote, PropertyNames::BrewNote::projAtten , 2); + SMART_FIELD_INIT(BrewNoteWidget, label_yeastAtten , lcdnumber_atten , BrewNote, PropertyNames::BrewNote::attenuation , 2); + connect(this->lineEdit_Sg, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateSG ); connect(this->lineEdit_volIntoBk, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateVolumeIntoBK_l ); @@ -97,21 +97,23 @@ void BrewNoteWidget::updateDateFormat() { void BrewNoteWidget::updateProjOg() { - // Density UnitSystems only have one scale, so we don't bother looking up UnitSystem::RelativeScale - auto forcedSystemOfMeasurement = this->label_projectedOg->getForcedSystemOfMeasurement(); - double quant = Measurement::amountDisplay(Measurement::Amount{this->bNoteObs->projOg(), Measurement::Units::specificGravity}, - forcedSystemOfMeasurement); + // SmartDigitWidget::setLowLim and SmartDigitWidget::setHighLim take their parameter in canonical units -- in this + // case, SG. + double const quant = this->bNoteObs->projOg(); this->lcdnumber_projectedOG->setLowLim( lowLimitPct * quant); this->lcdnumber_projectedOG->setHighLim(highLimitPct * quant); Measurement::UnitSystem const & displayUnitSystem = this->label_projectedOg->getDisplayUnitSystem(); int precision = (displayUnitSystem == Measurement::UnitSystems::density_Plato) ? 0 : 3; - this->lcdnumber_projectedOG->display(quant, precision); + // Set precision before setting amount as setPrecision does not update the display, whereas setAmount does + this->lcdnumber_projectedOG->setPrecision(precision); + this->lcdnumber_projectedOG->setAmount(quant); return; } void BrewNoteWidget::setBrewNote(BrewNote* bNote) { + qDebug() << Q_FUNC_INFO << "BrewNote:" << bNote; if (this->bNoteObs) { disconnect(this->bNoteObs, nullptr, this, nullptr); @@ -122,28 +124,28 @@ void BrewNoteWidget::setBrewNote(BrewNote* bNote) { connect(this->bNoteObs, &NamedEntity::changed, this, &BrewNoteWidget::changed); // Set the highs and the lows for the lcds - lcdnumber_effBK->setLowLim (bNoteObs->projEff_pct() * lowLimitPct); - lcdnumber_effBK->setHighLim(bNoteObs->projEff_pct() * highLimitPct); + this->lcdnumber_effBK->setLowLim (bNoteObs->projEff_pct() * lowLimitPct); + this->lcdnumber_effBK->setHighLim(bNoteObs->projEff_pct() * highLimitPct); - lcdnumber_projectedOG->setLowLim (bNoteObs->projOg() * lowLimitPct); - lcdnumber_projectedOG->setHighLim(bNoteObs->projOg() * highLimitPct); + this->lcdnumber_projectedOG->setLowLim (bNoteObs->projOg() * lowLimitPct); + this->lcdnumber_projectedOG->setHighLim(bNoteObs->projOg() * highLimitPct); - lcdnumber_brewhouseEff->setLowLim (bNoteObs->projEff_pct() * lowLimitPct); - lcdnumber_brewhouseEff->setHighLim(bNoteObs->projEff_pct() * highLimitPct); + this->lcdnumber_brewhouseEff->setLowLim (bNoteObs->projEff_pct() * lowLimitPct); + this->lcdnumber_brewhouseEff->setHighLim(bNoteObs->projEff_pct() * highLimitPct); - lcdnumber_projABV->setLowLim (bNoteObs->projABV_pct() * lowLimitPct); - lcdnumber_projABV->setHighLim(bNoteObs->projABV_pct() * highLimitPct); + this->lcdnumber_projABV->setLowLim (bNoteObs->projABV_pct() * lowLimitPct); + this->lcdnumber_projABV->setHighLim(bNoteObs->projABV_pct() * highLimitPct); - lcdnumber_abv->setLowLim (bNoteObs->projABV_pct() * lowLimitPct); - lcdnumber_abv->setHighLim(bNoteObs->projABV_pct() * highLimitPct); + this->lcdnumber_abv->setLowLim (bNoteObs->projABV_pct() * lowLimitPct); + this->lcdnumber_abv->setHighLim(bNoteObs->projABV_pct() * highLimitPct); - lcdnumber_atten->setLowLim (bNoteObs->projAtten() * lowLimitPct); - lcdnumber_atten->setHighLim(bNoteObs->projAtten() * highLimitPct); + this->lcdnumber_atten->setLowLim (bNoteObs->projAtten() * lowLimitPct); + this->lcdnumber_atten->setHighLim(bNoteObs->projAtten() * highLimitPct); - lcdnumber_projAtten->setLowLim (bNoteObs->projAtten() * lowLimitPct); - lcdnumber_projAtten->setHighLim(bNoteObs->projAtten() * highLimitPct); + this->lcdnumber_projAtten->setLowLim (bNoteObs->projAtten() * lowLimitPct); + this->lcdnumber_projAtten->setHighLim(bNoteObs->projAtten() * highLimitPct); - showChanges(); + this->showChanges(); } return; } @@ -189,21 +191,21 @@ void BrewNoteWidget::showChanges([[maybe_unused]] QString field) { this->lineEdit_volIntoFerm->setAmount (bNoteObs->volumeIntoFerm_l()); this->lineEdit_pitchTemp ->setAmount (bNoteObs->pitchTemp_c ()); this->lineEdit_Fg ->setAmount (bNoteObs->fg ()); - this->lineEdit_finalVolume ->setAmount (bNoteObs->finalVolume_l ()); + this->lineEdit_finalVolume->setAmount (bNoteObs->finalVolume_l ()); this->lineEdit_fermentDate->setDate (bNoteObs->fermentDate ()); this->btTextEdit_brewNotes->setPlainText(bNoteObs->notes ()); // Now with the calculated stuff - this->lcdnumber_effBK->display(bNoteObs->effIntoBK_pct(),2); + this->lcdnumber_effBK->setAmount(bNoteObs->effIntoBK_pct()); // Need to think about these? Maybe use the bubbles? this->updateProjOg(); // this requires more work, but updateProj does it - this->lcdnumber_brewhouseEff->display(bNoteObs->brewhouseEff_pct(), 2); - this->lcdnumber_projABV ->display(bNoteObs->projABV_pct (), 2); - this->lcdnumber_abv ->display(bNoteObs->abv (), 2); - this->lcdnumber_atten ->display(bNoteObs->attenuation (), 2); - this->lcdnumber_projAtten ->display(bNoteObs->projAtten (), 2); + this->lcdnumber_brewhouseEff->setAmount(bNoteObs->brewhouseEff_pct()); + this->lcdnumber_projABV ->setAmount(bNoteObs->projABV_pct ()); + this->lcdnumber_abv ->setAmount(bNoteObs->abv ()); + this->lcdnumber_atten ->setAmount(bNoteObs->attenuation ()); + this->lcdnumber_projAtten ->setAmount(bNoteObs->projAtten ()); return; } diff --git a/src/BtTreeModel.cpp b/src/BtTreeModel.cpp index b95f3235..e68d7c42 100644 --- a/src/BtTreeModel.cpp +++ b/src/BtTreeModel.cpp @@ -528,6 +528,7 @@ bool BtTreeModel::removeRows(int row, int count, const QModelIndex & parent) { // One find method for all things. This .. is nice QModelIndex BtTreeModel::findElement(NamedEntity * thing, BtTreeItem * parent) { + qDebug() << Q_FUNC_INFO << "Find" << thing << "in" << parent; BtTreeItem * pItem; QList folders; @@ -551,6 +552,7 @@ QModelIndex BtTreeModel::findElement(NamedEntity * thing, BtTreeItem * parent) { for (i = 0; i < target->childCount(); ++i) { // If we've found what we are looking for, return if (target->child(i)->thing() == thing) { + qDebug() << Q_FUNC_INFO << "Found at" << i; return createIndex(i, 0, target->child(i)); } diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 590482ab..bff9aeee 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -215,7 +215,6 @@ namespace { return; } - } // This private implementation class holds all private non-virtual members of MainWindow @@ -1150,8 +1149,7 @@ void MainWindow::setBrewNote(BrewNote* bNote) QString tabname; BrewNoteWidget* ni = findBrewNoteWidget(bNote); - if ( ni ) - { + if ( ni ) { tabWidget_recipeView->setCurrentWidget(ni); return; } @@ -1159,8 +1157,9 @@ void MainWindow::setBrewNote(BrewNote* bNote) ni = new BrewNoteWidget(tabWidget_recipeView); ni->setBrewNote(bNote); - tabWidget_recipeView->addTab(ni,bNote->brewDate_short()); - tabWidget_recipeView->setCurrentWidget(ni); + this->tabWidget_recipeView->addTab(ni,bNote->brewDate_short()); + this->tabWidget_recipeView->setCurrentWidget(ni); + return; } void MainWindow::setAncestor() @@ -1390,11 +1389,14 @@ void MainWindow::showChanges(QMetaProperty* prop) { this->lineEdit_boilSg->setAmount(this->recipeObs->boilGrav()); Style const * style = this->recipeObs->style(); - - updateDensitySlider(*this->styleRangeWidget_og, *this->oGLabel, style->ogMin(), style->ogMax(), 1.120); + if (style) { + updateDensitySlider(*this->styleRangeWidget_og, *this->oGLabel, style->ogMin(), style->ogMax(), 1.120); + } this->styleRangeWidget_og->setValue(this->oGLabel->getAmountToDisplay(recipeObs->og())); - updateDensitySlider(*this->styleRangeWidget_fg, *this->fGLabel, style->fgMin(), style->fgMax(), 1.030); + if (style) { + updateDensitySlider(*this->styleRangeWidget_fg, *this->fGLabel, style->fgMin(), style->fgMax(), 1.030); + } this->styleRangeWidget_fg->setValue(this->fGLabel->getAmountToDisplay(recipeObs->fg())); this->styleRangeWidget_abv->setValue(recipeObs->ABV_pct()); @@ -1413,10 +1415,12 @@ void MainWindow::showChanges(QMetaProperty* prop) { this->rangeWidget_boilsize->setValue (this->label_boilSize->getAmountToDisplay(this->recipeObs->boilVolume_l())); /* Colors need the same basic treatment as gravity */ - updateColorSlider(*this->styleRangeWidget_srm, - *this->colorSRMLabel, - style->colorMin_srm(), - style->colorMax_srm()); + if (style) { + updateColorSlider(*this->styleRangeWidget_srm, + *this->colorSRMLabel, + style->colorMin_srm(), + style->colorMax_srm()); + } this->styleRangeWidget_srm->setValue(this->colorSRMLabel->getAmountToDisplay(this->recipeObs->color_srm())); // In some, incomplete, recipes, OG is approximately 1.000, which then makes GU close to 0 and thus IBU/GU insanely @@ -2399,8 +2403,8 @@ void MainWindow::newBrewNote() { bIndex = treeView_recipe->findElement(bNote.get()); if ( bIndex.isValid() ) setTreeSelection(bIndex); + } } -} void MainWindow::reBrewNote() { QModelIndexList indexes = treeView_recipe->selectionModel()->selectedRows(); diff --git a/src/WaterDialog.cpp b/src/WaterDialog.cpp index 36a170f0..2f6a248b 100644 --- a/src/WaterDialog.cpp +++ b/src/WaterDialog.cpp @@ -78,9 +78,6 @@ WaterDialog::WaterDialog(QWidget* parent) : m_spargeRO{0.0}, m_total_grains{0.0}, m_thickness{0.0} { - QStringList msgs = QStringList() << tr("Too low for target profile.") - << tr("In range for target profile.") - << tr("Too high for target profile."); setupUi(this); // initialize the two buttons and lists (I think) @@ -100,36 +97,53 @@ WaterDialog::WaterDialog(QWidget* parent) : m_target_filter->sort(0); targetProfileCombo->setModel(m_target_filter); + SMART_FIELD_INIT_FS(WaterDialog, label_ca , btDigit_ca , double, Measurement::PhysicalQuantity::VolumeConcentration, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_cl , btDigit_cl , double, Measurement::PhysicalQuantity::VolumeConcentration, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_hco3, btDigit_hco3, double, Measurement::PhysicalQuantity::VolumeConcentration, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_mg , btDigit_mg , double, Measurement::PhysicalQuantity::VolumeConcentration, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_na , btDigit_na , double, Measurement::PhysicalQuantity::VolumeConcentration, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_so4 , btDigit_so4 , double, Measurement::PhysicalQuantity::VolumeConcentration, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_pH , btDigit_ph , double, Measurement::PhysicalQuantity::Acidity , 1); + + SMART_FIELD_INIT_FS(WaterDialog, label_totalcacl2 , btDigit_totalcacl2 , double, Measurement::PhysicalQuantity::Mass, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_totalcaco3 , btDigit_totalcaco3 , double, Measurement::PhysicalQuantity::Mass, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_totalcaso4 , btDigit_totalcaso4 , double, Measurement::PhysicalQuantity::Mass, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_totalmgso4 , btDigit_totalmgso4 , double, Measurement::PhysicalQuantity::Mass, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_totalnacl , btDigit_totalnacl , double, Measurement::PhysicalQuantity::Mass, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_totalnahco3, btDigit_totalnahco3, double, Measurement::PhysicalQuantity::Mass, 2); + // not sure if this is better or worse, but we will try it out - m_ppm_digits[static_cast(Water::Ions::Ca)] = btDigit_ca; - m_ppm_digits[static_cast(Water::Ions::Cl)] = btDigit_cl; + m_ppm_digits[static_cast(Water::Ions::Ca)] = btDigit_ca ; + m_ppm_digits[static_cast(Water::Ions::Cl)] = btDigit_cl ; m_ppm_digits[static_cast(Water::Ions::HCO3)] = btDigit_hco3; - m_ppm_digits[static_cast(Water::Ions::Mg)] = btDigit_mg; - m_ppm_digits[static_cast(Water::Ions::Na)] = btDigit_na; - m_ppm_digits[static_cast(Water::Ions::SO4)] = btDigit_so4; - - m_total_digits[static_cast(Salt::Types::CACL2 )] = btDigit_totalcacl2; - m_total_digits[static_cast(Salt::Types::CACO3 )] = btDigit_totalcaco3; - m_total_digits[static_cast(Salt::Types::CASO4 )] = btDigit_totalcaso4; - m_total_digits[static_cast(Salt::Types::MGSO4 )] = btDigit_totalmgso4; - m_total_digits[static_cast(Salt::Types::NACL )] = btDigit_totalnacl; + m_ppm_digits[static_cast(Water::Ions::Mg)] = btDigit_mg ; + m_ppm_digits[static_cast(Water::Ions::Na)] = btDigit_na ; + m_ppm_digits[static_cast(Water::Ions::SO4)] = btDigit_so4 ; + + m_total_digits[static_cast(Salt::Types::CACL2 )] = btDigit_totalcacl2 ; + m_total_digits[static_cast(Salt::Types::CACO3 )] = btDigit_totalcaco3 ; + m_total_digits[static_cast(Salt::Types::CASO4 )] = btDigit_totalcaso4 ; + m_total_digits[static_cast(Salt::Types::MGSO4 )] = btDigit_totalmgso4 ; + m_total_digits[static_cast(Salt::Types::NACL )] = btDigit_totalnacl ; m_total_digits[static_cast(Salt::Types::NAHCO3)] = btDigit_totalnahco3; // foreach( SmartDigitWidget* i, m_ppm_digits ) { for (int i = 0; i < static_cast(Water::Ions::numIons); ++i ) { m_ppm_digits[i]->setLimits(0.0,1000.0); - m_ppm_digits[i]->setText(0.0, 1); - m_ppm_digits[i]->setMessages(msgs); + m_ppm_digits[i]->setAmount(0.0); + m_ppm_digits[i]->setMessages(tr("Too low for target profile."), + tr("In range for target profile."), + tr("Too high for target profile.")); } // we can be a bit more specific with pH btDigit_ph->setLowLim(5.0); btDigit_ph->setHighLim(5.5); - btDigit_ph->display(7.0,1); + btDigit_ph->setAmount(7.0); // since all the things are now digits, lets get the totals configured for (int i = static_cast(Salt::Types::CACL2); i < static_cast(Salt::Types::NAHCO3); ++i ) { - m_total_digits[i]->setConstantColor(SmartDigitWidget::BLACK); - m_total_digits[i]->setText(0.0,1); + m_total_digits[i]->setConstantColor(SmartDigitWidget::ColorType::Black); + m_total_digits[i]->setAmount(0.0); } // and now let's see what the table does. m_salt_table_model = new SaltTableModel(tableView_salts); @@ -140,13 +154,6 @@ WaterDialog::WaterDialog(QWidget* parent) : m_base_editor = new WaterEditor(this, "Base"); m_target_editor = new WaterEditor(this, "Target"); - SMART_FIELD_INIT_FS(WaterDialog, label_totalcacl2 , btDigit_totalcacl2 , double, Measurement::PhysicalQuantity::Mass, 2); - SMART_FIELD_INIT_FS(WaterDialog, label_totalcaco3 , btDigit_totalcaco3 , double, Measurement::PhysicalQuantity::Mass, 2); - SMART_FIELD_INIT_FS(WaterDialog, label_totalcaso4 , btDigit_totalcaso4 , double, Measurement::PhysicalQuantity::Mass, 2); - SMART_FIELD_INIT_FS(WaterDialog, label_totalmgso4 , btDigit_totalmgso4 , double, Measurement::PhysicalQuantity::Mass, 2); - SMART_FIELD_INIT_FS(WaterDialog, label_totalnacl , btDigit_totalnacl , double, Measurement::PhysicalQuantity::Mass, 2); - SMART_FIELD_INIT_FS(WaterDialog, label_totalnahco3, btDigit_totalnahco3, double, Measurement::PhysicalQuantity::Mass, 2); - // all the signals connect(baseProfileCombo, QOverload::of(&QComboBox::activated), this, &WaterDialog::update_baseProfile ); connect(targetProfileCombo, QOverload::of(&QComboBox::activated), this, &WaterDialog::update_targetProfile); @@ -192,12 +199,10 @@ void WaterDialog::setDigits() { double ppm = this->m_target->ppm(static_cast(i)); double min_ppm = ppm * 0.95; double max_ppm = ppm * 1.05; - QStringList msgs = QStringList() - << tr("Minimum expected concentration is %1 ppm").arg(min_ppm) - << tr("In range for target profile.") - << tr("Maximum expected concentration is %1 ppm").arg(max_ppm); m_ppm_digits[i]->setLimits(min_ppm,max_ppm); - m_ppm_digits[i]->setMessages(msgs); + m_ppm_digits[i]->setMessages(tr("Minimum expected concentration is %1 ppm").arg(min_ppm), + tr("In range for target profile."), + tr("Maximum expected concentration is %1 ppm").arg(max_ppm)); } // oddly, pH doesn't change with the target water @@ -205,7 +210,7 @@ void WaterDialog::setDigits() { } void WaterDialog::setRecipe(Recipe *rec) { - if ( rec == nullptr ) { + if (!rec) { return; } @@ -265,7 +270,7 @@ void WaterDialog::setRecipe(Recipe *rec) { if (ii->amountIsWeight()) { double lovi = ( ii->color_srm() +0.6 ) / 1.35; m_weighted_colors += (ii->amount()/m_total_grains)*lovi; - } + } break; case Fermentable::Type::Sugar: case Fermentable::Type::Other_Adjunct: @@ -369,7 +374,7 @@ void WaterDialog::newTotals() { for (int i = static_cast(Salt::Types::CACL2); i < static_cast(Salt::Types::LACTIC); ++i ) { Salt::Types type = static_cast(i); - m_total_digits[i]->setText(m_salt_table_model->total(type), 2); + m_total_digits[i]->setAmount(m_salt_table_model->total(type)); } // the total_* method return the numerator, we supply the denominator and @@ -387,15 +392,15 @@ void WaterDialog::newTotals() { for (int i = 0; i < static_cast(Water::Ions::numIons); ++i ) { Water::Ions ion = static_cast(i); double mPPM = modifier * this->m_base->ppm(ion); - m_ppm_digits[i]->setText( m_salt_table_model->total(ion) / allTheWaters + mPPM, 0 ); + m_ppm_digits[i]->setAmount( m_salt_table_model->total(ion) / allTheWaters + mPPM); } - btDigit_ph->setText( calculateMashpH(), 2 ); + btDigit_ph->setAmount(calculateMashpH()); } else { for (int i = 0; i < static_cast(Water::Ions::numIons); ++i ) { Water::Ions ion = static_cast(i); - m_ppm_digits[i]->setText( m_salt_table_model->total(ion) / allTheWaters, 0 ); + m_ppm_digits[i]->setAmount( m_salt_table_model->total(ion) / allTheWaters); } } return; @@ -518,7 +523,7 @@ double WaterDialog::calculateGristpH() { double lovi = 19.0; if ( ii->color_srm() <= 120 ) { lovi = ( ii->color_srm() + 0.6)/1.35; - } + } colorFromGrain = ( ii->amount() / m_total_grains ) * lovi; } break; diff --git a/src/database/DatabaseSchemaHelper.cpp b/src/database/DatabaseSchemaHelper.cpp index 7a53ef4d..cac4ed46 100644 --- a/src/database/DatabaseSchemaHelper.cpp +++ b/src/database/DatabaseSchemaHelper.cpp @@ -567,23 +567,30 @@ namespace { {QString(" UPDATE fermentable SET ftype = 'extract' WHERE ftype = 'Extract'" )}, {QString(" UPDATE fermentable SET ftype = 'dry extract' WHERE ftype = 'Dry Extract'")}, {QString(" UPDATE fermentable SET ftype = 'other' WHERE ftype = 'Adjunct'" )}, - {QString("ALTER TABLE fermentable ADD COLUMN grain_group %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN producer %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN productId %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN fineGrindYield_pct %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN coarseGrindYield_pct %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN potentialYield_sg %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN alphaAmylase_dextUnits %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN kolbachIndex_pct %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN amountIsWeight %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN hardnessPrpGlassy_pct %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN hardnessPrpHalf_pct %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN hardnessPrpMealy_pct %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN kernelSizePrpPlump_pct %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN kernelSizePrpThin_pct %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN friability_pct %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN di_ph %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN viscosity_cP %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN grain_group %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN producer %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN productId %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN fineGrindYield_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN coarseGrindYield_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN potentialYield_sg %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN alphaAmylase_dextUnits %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN kolbachIndex_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN amountIsWeight %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN hardnessPrpGlassy_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN hardnessPrpHalf_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN hardnessPrpMealy_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN kernelSizePrpPlump_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN kernelSizePrpThin_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN friability_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN di_ph %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN viscosity_cP %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN dmsP %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN dmsPIsMassPerVolume %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN fan %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN fanIsMassPerVolume %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN fermentability_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN betaGlucan %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN betaGlucanIsMassPerVolume %1").arg(db.getDbNativeTypeName())}, {QString(" UPDATE fermentable SET amountIsWeight = ?"), {QVariant{true}}}, // All existing amounts will be weights }; return executeSqlQueries(q, migrationQueries); diff --git a/src/database/ObjectStoreTyped.cpp b/src/database/ObjectStoreTyped.cpp index 611b17b1..90dd85d2 100644 --- a/src/database/ObjectStoreTyped.cpp +++ b/src/database/ObjectStoreTyped.cpp @@ -133,22 +133,29 @@ namespace { {ObjectStore::FieldType::Bool, "recommend_mash" , PropertyNames::Fermentable::recommendMash }, {ObjectStore::FieldType::Double, "yield" , PropertyNames::Fermentable::yield_pct }, // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - {ObjectStore::FieldType::Enum, "grain_group" , PropertyNames::Fermentable::grainGroup, &Fermentable::grainGroupStringMapping}, - {ObjectStore::FieldType::String, "producer" , PropertyNames::Fermentable::producer }, - {ObjectStore::FieldType::String, "productId" , PropertyNames::Fermentable::productId }, - {ObjectStore::FieldType::Double, "fineGrindYield_pct" , PropertyNames::Fermentable::fineGrindYield_pct }, - {ObjectStore::FieldType::Double, "coarseGrindYield_pct" , PropertyNames::Fermentable::coarseGrindYield_pct }, - {ObjectStore::FieldType::Double, "potentialYield_sg" , PropertyNames::Fermentable::potentialYield_sg }, - {ObjectStore::FieldType::Double, "alphaAmylase_dextUnits", PropertyNames::Fermentable::alphaAmylase_dextUnits }, - {ObjectStore::FieldType::Double, "kolbachIndex_pct" , PropertyNames::Fermentable::kolbachIndex_pct }, - {ObjectStore::FieldType::Double, "hardnessPrpGlassy_pct" , PropertyNames::Fermentable::hardnessPrpGlassy_pct }, - {ObjectStore::FieldType::Double, "hardnessPrpHalf_pct" , PropertyNames::Fermentable::hardnessPrpHalf_pct }, - {ObjectStore::FieldType::Double, "hardnessPrpMealy_pct" , PropertyNames::Fermentable::hardnessPrpMealy_pct }, - {ObjectStore::FieldType::Double, "kernelSizePrpPlump_pct", PropertyNames::Fermentable::kernelSizePrpPlump_pct }, - {ObjectStore::FieldType::Double, "kernelSizePrpThin_pct" , PropertyNames::Fermentable::kernelSizePrpThin_pct }, - {ObjectStore::FieldType::Double, "friability_pct" , PropertyNames::Fermentable::friability_pct }, - {ObjectStore::FieldType::Double, "di_ph" , PropertyNames::Fermentable::di_ph }, - {ObjectStore::FieldType::Double, "viscosity_cP" , PropertyNames::Fermentable::viscosity_cP }, + {ObjectStore::FieldType::Enum, "grain_group" , PropertyNames::Fermentable::grainGroup, &Fermentable::grainGroupStringMapping}, + {ObjectStore::FieldType::String, "producer" , PropertyNames::Fermentable::producer }, + {ObjectStore::FieldType::String, "productId" , PropertyNames::Fermentable::productId }, + {ObjectStore::FieldType::Double, "fineGrindYield_pct" , PropertyNames::Fermentable::fineGrindYield_pct }, + {ObjectStore::FieldType::Double, "coarseGrindYield_pct" , PropertyNames::Fermentable::coarseGrindYield_pct }, + {ObjectStore::FieldType::Double, "potentialYield_sg" , PropertyNames::Fermentable::potentialYield_sg }, + {ObjectStore::FieldType::Double, "alphaAmylase_dextUnits" , PropertyNames::Fermentable::alphaAmylase_dextUnits }, + {ObjectStore::FieldType::Double, "kolbachIndex_pct" , PropertyNames::Fermentable::kolbachIndex_pct }, + {ObjectStore::FieldType::Double, "hardnessPrpGlassy_pct" , PropertyNames::Fermentable::hardnessPrpGlassy_pct }, + {ObjectStore::FieldType::Double, "hardnessPrpHalf_pct" , PropertyNames::Fermentable::hardnessPrpHalf_pct }, + {ObjectStore::FieldType::Double, "hardnessPrpMealy_pct" , PropertyNames::Fermentable::hardnessPrpMealy_pct }, + {ObjectStore::FieldType::Double, "kernelSizePrpPlump_pct" , PropertyNames::Fermentable::kernelSizePrpPlump_pct }, + {ObjectStore::FieldType::Double, "kernelSizePrpThin_pct" , PropertyNames::Fermentable::kernelSizePrpThin_pct }, + {ObjectStore::FieldType::Double, "friability_pct" , PropertyNames::Fermentable::friability_pct }, + {ObjectStore::FieldType::Double, "di_ph" , PropertyNames::Fermentable::di_ph }, + {ObjectStore::FieldType::Double, "viscosity_cP" , PropertyNames::Fermentable::viscosity_cP }, + {ObjectStore::FieldType::Double, "dmsP" , PropertyNames::Fermentable::dmsP }, + {ObjectStore::FieldType::Bool , "dmsPIsMassPerVolume" , PropertyNames::Fermentable::dmsPIsMassPerVolume }, + {ObjectStore::FieldType::Double, "fan" , PropertyNames::Fermentable::fan }, + {ObjectStore::FieldType::Bool , "fanIsMassPerVolume" , PropertyNames::Fermentable::fanIsMassPerVolume }, + {ObjectStore::FieldType::Double, "fermentability_pct" , PropertyNames::Fermentable::fermentability_pct }, + {ObjectStore::FieldType::Double, "betaGlucan" , PropertyNames::Fermentable::betaGlucan }, + {ObjectStore::FieldType::Bool , "betaGlucanIsMassPerVolume", PropertyNames::Fermentable::betaGlucanIsMassPerVolume}, } }; template<> ObjectStore::JunctionTableDefinitions const JUNCTION_TABLES { diff --git a/src/json/BeerJson.cpp b/src/json/BeerJson.cpp index 91f1d66d..480f1f74 100644 --- a/src/json/BeerJson.cpp +++ b/src/json/BeerJson.cpp @@ -292,12 +292,12 @@ namespace { {JsonRecordDefinition::FieldType::SingleUnitValue , "mealy" , PropertyNames::Fermentable::hardnessPrpMealy_pct , &BEER_JSON_PERCENT_UNIT }, {JsonRecordDefinition::FieldType::SingleUnitValue , "thru" , PropertyNames::Fermentable::kernelSizePrpThin_pct , &BEER_JSON_PERCENT_UNIT }, {JsonRecordDefinition::FieldType::SingleUnitValue , "friability" , PropertyNames::Fermentable::friability_pct , &BEER_JSON_PERCENT_UNIT }, - {JsonRecordDefinition::FieldType::SingleUnitValue , "di_ph" , BtString::NULL_STR, &BEER_JSON_ACIDITY_UNIT }, // .:TODO.JSON:. Add this to Fermentable - {JsonRecordDefinition::FieldType::MeasurementWithUnits , "viscosity" , BtString::NULL_STR, &BEER_JSON_VISCOSITY_UNIT_MAPPER }, // .:TODO.JSON:. Add this to Fermentable - {JsonRecordDefinition::FieldType::OneOfMeasurementsWithUnits, "dms_p" , BtString::NULL_STR, &BEER_JSON_CONCENTRATION_UNIT_MAPPER }, // .:TODO.JSON:. Add this to Fermentable - {JsonRecordDefinition::FieldType::OneOfMeasurementsWithUnits, "fan" , BtString::NULL_STR, &BEER_JSON_CONCENTRATION_UNIT_MAPPER }, // .:TODO.JSON:. Add this to Fermentable - {JsonRecordDefinition::FieldType::SingleUnitValue , "fermentability" , BtString::NULL_STR, &BEER_JSON_PERCENT_UNIT }, // .:TODO.JSON:. Add this to Fermentable - {JsonRecordDefinition::FieldType::OneOfMeasurementsWithUnits, "beta_glucan" , BtString::NULL_STR, &BEER_JSON_CONCENTRATION_UNIT_MAPPER }, // .:TODO.JSON:. Add this to Fermentable + {JsonRecordDefinition::FieldType::SingleUnitValue , "di_ph" , PropertyNames::Fermentable::di_ph , &BEER_JSON_ACIDITY_UNIT }, + {JsonRecordDefinition::FieldType::MeasurementWithUnits , "viscosity" , PropertyNames::Fermentable::viscosity_cP , &BEER_JSON_VISCOSITY_UNIT_MAPPER }, + {JsonRecordDefinition::FieldType::OneOfMeasurementsWithUnits, "dms_p" , PropertyNames::Fermentable::dmsPWithUnits , &BEER_JSON_CONCENTRATION_UNIT_MAPPER }, + {JsonRecordDefinition::FieldType::OneOfMeasurementsWithUnits, "fan" , PropertyNames::Fermentable::fanWithUnits , &BEER_JSON_CONCENTRATION_UNIT_MAPPER }, + {JsonRecordDefinition::FieldType::SingleUnitValue , "fermentability" , PropertyNames::Fermentable::fermentability_pct , &BEER_JSON_PERCENT_UNIT }, + {JsonRecordDefinition::FieldType::OneOfMeasurementsWithUnits, "beta_glucan" , PropertyNames::Fermentable::betaGlucanWithUnits , &BEER_JSON_CONCENTRATION_UNIT_MAPPER }, }; // .:TODO.JSON:. Extend Recipe to have an enum for this EnumStringMapping const BEER_JSON_RECIPE_ADDITION_POINT_MAPPER { diff --git a/src/measurement/Amount.h b/src/measurement/Amount.h index 3c7cfee0..b8afa97e 100644 --- a/src/measurement/Amount.h +++ b/src/measurement/Amount.h @@ -59,41 +59,6 @@ namespace Measurement { } - -//// /** TODO We should template this so that we can then do using for massorvolume and massconcorvolconc -//// * \brief A version of \c Measurement::Amount that is "constrained" to be either a -//// * \c Measurement::PhysicalQuantity::Mass or a \c Measurement::PhysicalQuantity::Volume. -//// * The constraint is not bullet-proof but you will get an assert (on a debug build) if you try to construct / -//// * assign / move it with a \c Measurement::Unit of the wrong \c Measurement::PhysicalQuantity -//// */ -//// class MassOrVolumeAmt : public Measurement::Amount { -//// public: -//// /** -//// * \brief Default constructor is needed so we can store in \c QVariant which is needed to use this type in the Qt -//// * Properties system. The default-constructed type will be an invalid amount (eg a negative mass). -//// */ -//// MassOrVolumeAmt(); -//// -//// //! Regular constructor -//// MassOrVolumeAmt(double quantity, Measurement::Unit const & unit); -//// -//// //! Copy constructor -//// MassOrVolumeAmt(Measurement::Amount const & other); -//// -//// //! Assignment operator -//// MassOrVolumeAmt & operator=(Measurement::Amount const & other); -//// -//// //! Move constructor. -//// MassOrVolumeAmt(Measurement::Amount && other); -//// -//// //! Move assignment. -//// MassOrVolumeAmt & operator=(Measurement::Amount && other); -//// -//// bool isMass() const; -//// private: -//// bool wasConstructAssignOrMoveOK(); -//// }; - bool operator<(Measurement::Amount const & lhs, Measurement::Amount const & rhs); bool operator==(Measurement::Amount const & lhs, Measurement::Amount const & rhs); diff --git a/src/measurement/Unit.cpp b/src/measurement/Unit.cpp index 29a5e50f..82312809 100644 --- a/src/measurement/Unit.cpp +++ b/src/measurement/Unit.cpp @@ -263,6 +263,10 @@ Measurement::Unit const & Measurement::Unit::getCanonical() const { return Measurement::Unit::getCanonicalUnit(this->getPhysicalQuantity()); } +bool Measurement::Unit::isCanonical() const { + return &this->getCanonical() == this; +} + Measurement::Amount Measurement::Unit::toCanonical(double amt) const { return Measurement::Amount{this->pimpl->convertToCanonical(amt), this->getCanonical()}; } diff --git a/src/measurement/Unit.h b/src/measurement/Unit.h index b912cf36..db9ad345 100644 --- a/src/measurement/Unit.h +++ b/src/measurement/Unit.h @@ -106,6 +106,12 @@ namespace Measurement { */ Measurement::Unit const & getCanonical() const; + /** + * \brief In certain circumstances, we expect things to be in canonical units, so this is a useful function for + * checking or asserting that. + */ + bool isCanonical() const; + /** * \brief Convert an amount of this unit to its canonical system of measurement (usually, but not always, an SI or * other metric measure) diff --git a/src/model/Fermentable.cpp b/src/model/Fermentable.cpp index b144c388..e37c50c5 100644 --- a/src/model/Fermentable.cpp +++ b/src/model/Fermentable.cpp @@ -93,17 +93,17 @@ bool Fermentable::isEqualTo(NamedEntity const & other) const { Fermentable const & rhs = static_cast(other); // Base class will already have ensured names are equal return ( - this->m_type == rhs.m_type && - this->m_yield_pct == rhs.m_yield_pct && - this->m_color_srm == rhs.m_color_srm && - this->m_origin == rhs.m_origin && - this->m_supplier == rhs.m_supplier && - this->m_coarseFineDiff_pct == rhs.m_coarseFineDiff_pct && - this->m_moisture_pct == rhs.m_moisture_pct && + this->m_type == rhs.m_type && + this->m_yield_pct == rhs.m_yield_pct && + this->m_color_srm == rhs.m_color_srm && + this->m_origin == rhs.m_origin && + this->m_supplier == rhs.m_supplier && + this->m_coarseFineDiff_pct == rhs.m_coarseFineDiff_pct && + this->m_moisture_pct == rhs.m_moisture_pct && this->m_diastaticPower_lintner == rhs.m_diastaticPower_lintner && - this->m_protein_pct == rhs.m_protein_pct && - this->m_maxInBatch_pct == rhs.m_maxInBatch_pct && - this->m_grainGroup == rhs.m_grainGroup + this->m_protein_pct == rhs.m_protein_pct && + this->m_maxInBatch_pct == rhs.m_maxInBatch_pct && + this->m_grainGroup == rhs.m_grainGroup ); } @@ -114,40 +114,52 @@ ObjectStore & Fermentable::getObjectStoreTypedInstance() const { TypeLookup const Fermentable::typeLookup { "Fermentable", { - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::type , Fermentable::m_type ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::amount , Fermentable::m_amount , Measurement::PqEitherMassOrVolume ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::amountIsWeight , Fermentable::m_amountIsWeight ), // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::yield_pct , Fermentable::m_yield_pct , NonPhysicalQuantity::Percentage ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::color_srm , Fermentable::m_color_srm , Measurement::PhysicalQuantity::Color ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::addAfterBoil , Fermentable::m_addAfterBoil , NonPhysicalQuantity::Bool ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::origin , Fermentable::m_origin , NonPhysicalQuantity::String ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::supplier , Fermentable::m_supplier , NonPhysicalQuantity::String ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::notes , Fermentable::m_notes ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::coarseFineDiff_pct , Fermentable::m_coarseFineDiff_pct , NonPhysicalQuantity::Percentage ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::moisture_pct , Fermentable::m_moisture_pct , NonPhysicalQuantity::Percentage ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::diastaticPower_lintner, Fermentable::m_diastaticPower_lintner, Measurement::PhysicalQuantity::DiastaticPower), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::protein_pct , Fermentable::m_protein_pct , NonPhysicalQuantity::Percentage ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::maxInBatch_pct , Fermentable::m_maxInBatch_pct , NonPhysicalQuantity::Percentage ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::recommendMash , Fermentable::m_recommendMash , NonPhysicalQuantity::Bool ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::ibuGalPerLb , Fermentable::m_ibuGalPerLb , NonPhysicalQuantity::Dimensionless ), // Not really dimensionless... - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::isMashed , Fermentable::m_isMashed , NonPhysicalQuantity::Bool ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::type , Fermentable::m_type ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::amount , Fermentable::m_amount , Measurement::PqEitherMassOrVolume ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::amountIsWeight , Fermentable::m_amountIsWeight , NonPhysicalQuantity::Bool ), // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::yield_pct , Fermentable::m_yield_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::color_srm , Fermentable::m_color_srm , Measurement::PhysicalQuantity::Color ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::addAfterBoil , Fermentable::m_addAfterBoil , NonPhysicalQuantity::Bool ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::origin , Fermentable::m_origin , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::supplier , Fermentable::m_supplier , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::notes , Fermentable::m_notes ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::coarseFineDiff_pct , Fermentable::m_coarseFineDiff_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::moisture_pct , Fermentable::m_moisture_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::diastaticPower_lintner , Fermentable::m_diastaticPower_lintner , Measurement::PhysicalQuantity::DiastaticPower ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::protein_pct , Fermentable::m_protein_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::maxInBatch_pct , Fermentable::m_maxInBatch_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::recommendMash , Fermentable::m_recommendMash , NonPhysicalQuantity::Bool ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::ibuGalPerLb , Fermentable::m_ibuGalPerLb , NonPhysicalQuantity::Dimensionless ), // Not really dimensionless... + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::isMashed , Fermentable::m_isMashed , NonPhysicalQuantity::Bool ), // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::grainGroup , Fermentable::m_grainGroup ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::producer , Fermentable::m_producer , NonPhysicalQuantity::String ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::productId , Fermentable::m_productId , NonPhysicalQuantity::String ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::fineGrindYield_pct , Fermentable::m_fineGrindYield_pct , NonPhysicalQuantity::Percentage ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::coarseGrindYield_pct , Fermentable::m_coarseGrindYield_pct , NonPhysicalQuantity::Percentage ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::potentialYield_sg , Fermentable::m_potentialYield_sg , Measurement::PhysicalQuantity::Density ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::alphaAmylase_dextUnits, Fermentable::m_alphaAmylase_dextUnits, NonPhysicalQuantity::Dimensionless ), // Not really dimensionless... - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::kolbachIndex_pct , Fermentable::m_kolbachIndex_pct , NonPhysicalQuantity::Percentage ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::hardnessPrpGlassy_pct , Fermentable::m_hardnessPrpGlassy_pct , NonPhysicalQuantity::Percentage ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::hardnessPrpHalf_pct , Fermentable::m_hardnessPrpHalf_pct , NonPhysicalQuantity::Percentage ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::hardnessPrpMealy_pct , Fermentable::m_hardnessPrpMealy_pct , NonPhysicalQuantity::Percentage ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::kernelSizePrpPlump_pct, Fermentable::m_kernelSizePrpPlump_pct, NonPhysicalQuantity::Percentage ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::kernelSizePrpThin_pct , Fermentable::m_kernelSizePrpThin_pct , NonPhysicalQuantity::Percentage ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::friability_pct , Fermentable::m_friability_pct , NonPhysicalQuantity::Percentage ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::di_ph , Fermentable::m_di_ph , Measurement::PhysicalQuantity::Acidity ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::viscosity_cP , Fermentable::m_viscosity_cP , Measurement::PhysicalQuantity::Viscosity ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::grainGroup , Fermentable::m_grainGroup ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::producer , Fermentable::m_producer , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::productId , Fermentable::m_productId , NonPhysicalQuantity::String ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::fineGrindYield_pct , Fermentable::m_fineGrindYield_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::coarseGrindYield_pct , Fermentable::m_coarseGrindYield_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::potentialYield_sg , Fermentable::m_potentialYield_sg , Measurement::PhysicalQuantity::Density ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::alphaAmylase_dextUnits , Fermentable::m_alphaAmylase_dextUnits , NonPhysicalQuantity::Dimensionless ), // Not really dimensionless... + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::kolbachIndex_pct , Fermentable::m_kolbachIndex_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::hardnessPrpGlassy_pct , Fermentable::m_hardnessPrpGlassy_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::hardnessPrpHalf_pct , Fermentable::m_hardnessPrpHalf_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::hardnessPrpMealy_pct , Fermentable::m_hardnessPrpMealy_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::kernelSizePrpPlump_pct , Fermentable::m_kernelSizePrpPlump_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::kernelSizePrpThin_pct , Fermentable::m_kernelSizePrpThin_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::friability_pct , Fermentable::m_friability_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::di_ph , Fermentable::m_di_ph , Measurement::PhysicalQuantity::Acidity ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::viscosity_cP , Fermentable::m_viscosity_cP , Measurement::PhysicalQuantity::Viscosity ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::dmsP , Fermentable::m_dmsP , Measurement::PqEitherMassOrVolumeConcentration), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::dmsPIsMassPerVolume , Fermentable::m_dmsPIsMassPerVolume , NonPhysicalQuantity::Bool ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::fan , Fermentable::m_fan , Measurement::PqEitherMassOrVolumeConcentration), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::fanIsMassPerVolume , Fermentable::m_fanIsMassPerVolume , NonPhysicalQuantity::Bool ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::fermentability_pct , Fermentable::m_fermentability_pct , NonPhysicalQuantity::Percentage ), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::betaGlucan , Fermentable::m_betaGlucan , Measurement::PqEitherMassOrVolumeConcentration), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Fermentable::betaGlucanIsMassPerVolume, Fermentable::m_betaGlucanIsMassPerVolume, NonPhysicalQuantity::Bool ), + + PROPERTY_TYPE_LOOKUP_ENTRY_NO_MV(PropertyNames::Fermentable::amountWithUnits , Fermentable::amountWithUnits , Measurement::PqEitherMassOrVolume ), + PROPERTY_TYPE_LOOKUP_ENTRY_NO_MV(PropertyNames::Fermentable::dmsPWithUnits , Fermentable::dmsPWithUnits , Measurement::PqEitherMassOrVolumeConcentration), + PROPERTY_TYPE_LOOKUP_ENTRY_NO_MV(PropertyNames::Fermentable::fanWithUnits , Fermentable::fanWithUnits , Measurement::PqEitherMassOrVolumeConcentration), + PROPERTY_TYPE_LOOKUP_ENTRY_NO_MV(PropertyNames::Fermentable::betaGlucanWithUnits, Fermentable::betaGlucanWithUnits , Measurement::PqEitherMassOrVolumeConcentration), }, // Parent class lookup. NB: NamedEntityWithInventory not NamedEntity! &NamedEntityWithInventory::typeLookup @@ -155,173 +167,192 @@ TypeLookup const Fermentable::typeLookup { static_assert(std::is_base_of::value); Fermentable::Fermentable(QString name) : - NamedEntityWithInventory{name, true}, - m_type {Fermentable::Type::Grain}, - m_amount {0.0 }, - m_amountIsWeight {true }, // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ - m_yield_pct {0.0 }, - m_color_srm {0.0 }, - m_addAfterBoil {false }, - m_origin {"" }, - m_supplier {"" }, - m_notes {"" }, - m_coarseFineDiff_pct {0.0 }, - m_moisture_pct {0.0 }, - m_diastaticPower_lintner{0.0 }, - m_protein_pct {0.0 }, - m_maxInBatch_pct {100.0 }, - m_recommendMash {false }, - m_ibuGalPerLb {0.0 }, - m_isMashed {false }, - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - m_grainGroup {std::nullopt }, - m_producer {"" }, - m_productId {"" }, - m_fineGrindYield_pct {std::nullopt }, - m_coarseGrindYield_pct {std::nullopt }, - m_potentialYield_sg {std::nullopt }, - m_alphaAmylase_dextUnits{std::nullopt }, - m_kolbachIndex_pct {std::nullopt }, - m_hardnessPrpGlassy_pct {std::nullopt }, - m_hardnessPrpHalf_pct {std::nullopt }, - m_hardnessPrpMealy_pct {std::nullopt }, - m_kernelSizePrpPlump_pct{std::nullopt }, - m_kernelSizePrpThin_pct {std::nullopt }, - m_friability_pct {std::nullopt }, - m_di_ph {std::nullopt }, - m_viscosity_cP {std::nullopt } { + NamedEntityWithInventory {name, true}, + m_type {Fermentable::Type::Grain}, + m_amount {0.0 }, + m_amountIsWeight {true }, // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ + m_yield_pct {0.0 }, + m_color_srm {0.0 }, + m_addAfterBoil {false }, + m_origin {"" }, + m_supplier {"" }, + m_notes {"" }, + m_coarseFineDiff_pct {0.0 }, + m_moisture_pct {0.0 }, + m_diastaticPower_lintner {0.0 }, + m_protein_pct {0.0 }, + m_maxInBatch_pct {100.0 }, + m_recommendMash {false }, + m_ibuGalPerLb {0.0 }, + m_isMashed {false }, + // ⮜⮜⮜ All below added f or BeerJSON support ⮞⮞⮞ + m_grainGroup {std::nullopt }, + m_producer {"" }, + m_productId {"" }, + m_fineGrindYield_pct {std::nullopt }, + m_coarseGrindYield_pct {std::nullopt }, + m_potentialYield_sg {std::nullopt }, + m_alphaAmylase_dextUnits {std::nullopt }, + m_kolbachIndex_pct {std::nullopt }, + m_hardnessPrpGlassy_pct {std::nullopt }, + m_hardnessPrpHalf_pct {std::nullopt }, + m_hardnessPrpMealy_pct {std::nullopt }, + m_kernelSizePrpPlump_pct {std::nullopt }, + m_kernelSizePrpThin_pct {std::nullopt }, + m_friability_pct {std::nullopt }, + m_di_ph {std::nullopt }, + m_viscosity_cP {std::nullopt }, + m_dmsP {std::nullopt }, + m_dmsPIsMassPerVolume {true }, + m_fan {std::nullopt }, + m_fanIsMassPerVolume {true }, + m_fermentability_pct {std::nullopt }, + m_betaGlucan {std::nullopt }, + m_betaGlucanIsMassPerVolume{true } { return; } Fermentable::Fermentable(NamedParameterBundle const & namedParameterBundle) : - NamedEntityWithInventory{namedParameterBundle}, - m_type {namedParameterBundle.val(PropertyNames::Fermentable::type )}, - m_amount {namedParameterBundle.val(PropertyNames::Fermentable::amount )}, - m_amountIsWeight {namedParameterBundle.val(PropertyNames::Fermentable::amountIsWeight , true )}, // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ - m_yield_pct {namedParameterBundle.val(PropertyNames::Fermentable::yield_pct )}, - m_color_srm {namedParameterBundle.val(PropertyNames::Fermentable::color_srm )}, - m_addAfterBoil {namedParameterBundle.val(PropertyNames::Fermentable::addAfterBoil )}, - m_origin {namedParameterBundle.val(PropertyNames::Fermentable::origin , QString())}, - m_supplier {namedParameterBundle.val(PropertyNames::Fermentable::supplier , QString())}, - m_notes {namedParameterBundle.val(PropertyNames::Fermentable::notes , QString())}, - m_coarseFineDiff_pct {namedParameterBundle.val(PropertyNames::Fermentable::coarseFineDiff_pct )}, - m_moisture_pct {namedParameterBundle.val(PropertyNames::Fermentable::moisture_pct )}, - m_diastaticPower_lintner{namedParameterBundle.val(PropertyNames::Fermentable::diastaticPower_lintner )}, - m_protein_pct {namedParameterBundle.val(PropertyNames::Fermentable::protein_pct )}, - m_maxInBatch_pct {namedParameterBundle.val(PropertyNames::Fermentable::maxInBatch_pct )}, - m_recommendMash {namedParameterBundle.val(PropertyNames::Fermentable::recommendMash )}, - m_ibuGalPerLb {namedParameterBundle.val(PropertyNames::Fermentable::ibuGalPerLb )}, - m_isMashed {namedParameterBundle.val(PropertyNames::Fermentable::isMashed , false )}, + NamedEntityWithInventory {namedParameterBundle}, + m_type {namedParameterBundle.val(PropertyNames::Fermentable::type )}, + m_yield_pct {namedParameterBundle.val(PropertyNames::Fermentable::yield_pct )}, + m_color_srm {namedParameterBundle.val(PropertyNames::Fermentable::color_srm )}, + m_addAfterBoil {namedParameterBundle.val(PropertyNames::Fermentable::addAfterBoil )}, + m_origin {namedParameterBundle.val(PropertyNames::Fermentable::origin , QString())}, + m_supplier {namedParameterBundle.val(PropertyNames::Fermentable::supplier , QString())}, + m_notes {namedParameterBundle.val(PropertyNames::Fermentable::notes , QString())}, + m_coarseFineDiff_pct {namedParameterBundle.val(PropertyNames::Fermentable::coarseFineDiff_pct )}, + m_moisture_pct {namedParameterBundle.val(PropertyNames::Fermentable::moisture_pct )}, + m_diastaticPower_lintner {namedParameterBundle.val(PropertyNames::Fermentable::diastaticPower_lintner )}, + m_protein_pct {namedParameterBundle.val(PropertyNames::Fermentable::protein_pct )}, + m_maxInBatch_pct {namedParameterBundle.val(PropertyNames::Fermentable::maxInBatch_pct )}, + m_recommendMash {namedParameterBundle.val(PropertyNames::Fermentable::recommendMash )}, + m_ibuGalPerLb {namedParameterBundle.val(PropertyNames::Fermentable::ibuGalPerLb )}, + m_isMashed {namedParameterBundle.val(PropertyNames::Fermentable::isMashed , false )}, // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - m_grainGroup {namedParameterBundle.optEnumVal(PropertyNames::Fermentable::grainGroup )}, - m_producer {namedParameterBundle.val(PropertyNames::Fermentable::producer )}, - m_productId {namedParameterBundle.val(PropertyNames::Fermentable::productId )}, - m_fineGrindYield_pct {namedParameterBundle.val >(PropertyNames::Fermentable::fineGrindYield_pct )}, - m_coarseGrindYield_pct {namedParameterBundle.val >(PropertyNames::Fermentable::coarseGrindYield_pct )}, - m_potentialYield_sg {namedParameterBundle.val >(PropertyNames::Fermentable::potentialYield_sg )}, - m_alphaAmylase_dextUnits{namedParameterBundle.val >(PropertyNames::Fermentable::alphaAmylase_dextUnits )}, - m_kolbachIndex_pct {namedParameterBundle.val >(PropertyNames::Fermentable::kolbachIndex_pct )}, - m_hardnessPrpGlassy_pct {namedParameterBundle.val >(PropertyNames::Fermentable::hardnessPrpGlassy_pct )}, - m_hardnessPrpHalf_pct {namedParameterBundle.val >(PropertyNames::Fermentable::hardnessPrpHalf_pct )}, - m_hardnessPrpMealy_pct {namedParameterBundle.val >(PropertyNames::Fermentable::hardnessPrpMealy_pct )}, - m_kernelSizePrpPlump_pct{namedParameterBundle.val >(PropertyNames::Fermentable::kernelSizePrpPlump_pct )}, - m_kernelSizePrpThin_pct {namedParameterBundle.val >(PropertyNames::Fermentable::kernelSizePrpThin_pct )}, - m_friability_pct {namedParameterBundle.val >(PropertyNames::Fermentable::friability_pct )}, - m_di_ph {namedParameterBundle.val >(PropertyNames::Fermentable::di_ph )}, - m_viscosity_cP {namedParameterBundle.val >(PropertyNames::Fermentable::viscosity_cP )} { + m_grainGroup {namedParameterBundle.optEnumVal(PropertyNames::Fermentable::grainGroup )}, + m_producer {namedParameterBundle.val(PropertyNames::Fermentable::producer )}, + m_productId {namedParameterBundle.val(PropertyNames::Fermentable::productId )}, + m_fineGrindYield_pct {namedParameterBundle.val >(PropertyNames::Fermentable::fineGrindYield_pct )}, + m_coarseGrindYield_pct {namedParameterBundle.val >(PropertyNames::Fermentable::coarseGrindYield_pct )}, + m_potentialYield_sg {namedParameterBundle.val >(PropertyNames::Fermentable::potentialYield_sg )}, + m_alphaAmylase_dextUnits {namedParameterBundle.val >(PropertyNames::Fermentable::alphaAmylase_dextUnits )}, + m_kolbachIndex_pct {namedParameterBundle.val >(PropertyNames::Fermentable::kolbachIndex_pct )}, + m_hardnessPrpGlassy_pct {namedParameterBundle.val >(PropertyNames::Fermentable::hardnessPrpGlassy_pct )}, + m_hardnessPrpHalf_pct {namedParameterBundle.val >(PropertyNames::Fermentable::hardnessPrpHalf_pct )}, + m_hardnessPrpMealy_pct {namedParameterBundle.val >(PropertyNames::Fermentable::hardnessPrpMealy_pct )}, + m_kernelSizePrpPlump_pct {namedParameterBundle.val >(PropertyNames::Fermentable::kernelSizePrpPlump_pct )}, + m_kernelSizePrpThin_pct {namedParameterBundle.val >(PropertyNames::Fermentable::kernelSizePrpThin_pct )}, + m_friability_pct {namedParameterBundle.val >(PropertyNames::Fermentable::friability_pct )}, + m_di_ph {namedParameterBundle.val >(PropertyNames::Fermentable::di_ph )}, + m_viscosity_cP {namedParameterBundle.val >(PropertyNames::Fermentable::viscosity_cP )}, + m_fermentability_pct {namedParameterBundle.val >(PropertyNames::Fermentable::fermentability_pct )} { - if (namedParameterBundle.contains(*PropertyNames::Fermentable::amount)) { - this->m_amount = namedParameterBundle.val(PropertyNames::Fermentable::amount ); - this->m_amountIsWeight = namedParameterBundle.val(PropertyNames::Fermentable::amountIsWeight); - } else { - auto const massOrVolumeAmt = namedParameterBundle.val(PropertyNames::Fermentable::amountWithUnits); - // It is the caller's responsibility to have converted to canonical units, so we assert that either kg or liters - // are provided. - Q_ASSERT(&Measurement::Units::kilograms == massOrVolumeAmt.unit() || &Measurement::Units::liters == massOrVolumeAmt.unit()); - this->m_amount = massOrVolumeAmt.quantity(); - this->m_amountIsWeight = massOrVolumeAmt.isMass(); - } + this->setEitherOrReqParams(namedParameterBundle, PropertyNames::Fermentable::amount , PropertyNames::Fermentable::amountIsWeight , PropertyNames::Fermentable::amountWithUnits , this->m_amount , this->m_amountIsWeight ); + this->setEitherOrOptParams(namedParameterBundle, PropertyNames::Fermentable::dmsP , PropertyNames::Fermentable::dmsPIsMassPerVolume , PropertyNames::Fermentable::dmsPWithUnits , this->m_dmsP , this->m_dmsPIsMassPerVolume ); + this->setEitherOrOptParams(namedParameterBundle, PropertyNames::Fermentable::fan , PropertyNames::Fermentable::fanIsMassPerVolume , PropertyNames::Fermentable::fanWithUnits , this->m_fan , this->m_fanIsMassPerVolume ); + this->setEitherOrOptParams(namedParameterBundle, PropertyNames::Fermentable::betaGlucan, PropertyNames::Fermentable::betaGlucanIsMassPerVolume, PropertyNames::Fermentable::betaGlucanWithUnits, this->m_betaGlucan, this->m_betaGlucanIsMassPerVolume); return; } Fermentable::Fermentable(Fermentable const & other) : - NamedEntityWithInventory{other }, - m_type {other.m_type }, - m_amount {other.m_amount }, - m_amountIsWeight {other.m_amountIsWeight }, // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ - m_yield_pct {other.m_yield_pct }, - m_color_srm {other.m_color_srm }, - m_addAfterBoil {other.m_addAfterBoil }, - m_origin {other.m_origin }, - m_supplier {other.m_supplier }, - m_notes {other.m_notes }, - m_coarseFineDiff_pct {other.m_coarseFineDiff_pct }, - m_moisture_pct {other.m_moisture_pct }, - m_diastaticPower_lintner{other.m_diastaticPower_lintner}, - m_protein_pct {other.m_protein_pct }, - m_maxInBatch_pct {other.m_maxInBatch_pct }, - m_recommendMash {other.m_recommendMash }, - m_ibuGalPerLb {other.m_ibuGalPerLb }, - m_isMashed {other.m_isMashed }, + NamedEntityWithInventory {other }, + m_type {other.m_type }, + m_amount {other.m_amount }, + m_amountIsWeight {other.m_amountIsWeight }, // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ + m_yield_pct {other.m_yield_pct }, + m_color_srm {other.m_color_srm }, + m_addAfterBoil {other.m_addAfterBoil }, + m_origin {other.m_origin }, + m_supplier {other.m_supplier }, + m_notes {other.m_notes }, + m_coarseFineDiff_pct {other.m_coarseFineDiff_pct }, + m_moisture_pct {other.m_moisture_pct }, + m_diastaticPower_lintner {other.m_diastaticPower_lintner}, + m_protein_pct {other.m_protein_pct }, + m_maxInBatch_pct {other.m_maxInBatch_pct }, + m_recommendMash {other.m_recommendMash }, + m_ibuGalPerLb {other.m_ibuGalPerLb }, + m_isMashed {other.m_isMashed }, // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - m_grainGroup {other.m_grainGroup }, - m_producer {other.m_producer }, - m_productId {other.m_productId }, - m_fineGrindYield_pct {other.m_fineGrindYield_pct }, - m_coarseGrindYield_pct {other.m_coarseGrindYield_pct }, - m_potentialYield_sg {other.m_potentialYield_sg }, - m_alphaAmylase_dextUnits{other.m_alphaAmylase_dextUnits}, - m_kolbachIndex_pct {other.m_kolbachIndex_pct }, - m_hardnessPrpGlassy_pct {other.m_hardnessPrpGlassy_pct }, - m_hardnessPrpHalf_pct {other.m_hardnessPrpHalf_pct }, - m_hardnessPrpMealy_pct {other.m_hardnessPrpMealy_pct }, - m_kernelSizePrpPlump_pct{other.m_kernelSizePrpPlump_pct}, - m_kernelSizePrpThin_pct {other.m_kernelSizePrpThin_pct }, - m_friability_pct {other.m_friability_pct }, - m_di_ph {other.m_di_ph }, - m_viscosity_cP {other.m_viscosity_cP } { + m_grainGroup {other.m_grainGroup }, + m_producer {other.m_producer }, + m_productId {other.m_productId }, + m_fineGrindYield_pct {other.m_fineGrindYield_pct }, + m_coarseGrindYield_pct {other.m_coarseGrindYield_pct }, + m_potentialYield_sg {other.m_potentialYield_sg }, + m_alphaAmylase_dextUnits {other.m_alphaAmylase_dextUnits}, + m_kolbachIndex_pct {other.m_kolbachIndex_pct }, + m_hardnessPrpGlassy_pct {other.m_hardnessPrpGlassy_pct }, + m_hardnessPrpHalf_pct {other.m_hardnessPrpHalf_pct }, + m_hardnessPrpMealy_pct {other.m_hardnessPrpMealy_pct }, + m_kernelSizePrpPlump_pct {other.m_kernelSizePrpPlump_pct}, + m_kernelSizePrpThin_pct {other.m_kernelSizePrpThin_pct }, + m_friability_pct {other.m_friability_pct }, + m_di_ph {other.m_di_ph }, + m_viscosity_cP {other.m_viscosity_cP }, + m_dmsP {other.m_dmsP }, + m_dmsPIsMassPerVolume {other.m_dmsPIsMassPerVolume }, + m_fan {other.m_fan }, + m_fanIsMassPerVolume {other.m_fanIsMassPerVolume }, + m_fermentability_pct {other.m_fermentability_pct }, + m_betaGlucan {other.m_betaGlucan }, + m_betaGlucanIsMassPerVolume{other.m_betaGlucanIsMassPerVolume} { return; } Fermentable::~Fermentable() = default; //============================================= "GETTER" MEMBER FUNCTIONS ============================================== -Fermentable::Type Fermentable::type() const { return this->m_type ; } -double Fermentable::amount() const { return this->m_amount ; } -bool Fermentable::amountIsWeight() const { return this->m_amountIsWeight ; } // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ -double Fermentable::yield_pct() const { return this->m_yield_pct ; } -double Fermentable::color_srm() const { return this->m_color_srm ; } -bool Fermentable::addAfterBoil() const { return this->m_addAfterBoil ; } -QString Fermentable::origin() const { return this->m_origin ; } -QString Fermentable::supplier() const { return this->m_supplier ; } -QString Fermentable::notes() const { return this->m_notes ; } -double Fermentable::coarseFineDiff_pct() const { return this->m_coarseFineDiff_pct ; } -double Fermentable::moisture_pct() const { return this->m_moisture_pct ; } -double Fermentable::diastaticPower_lintner() const { return this->m_diastaticPower_lintner ; } -double Fermentable::protein_pct() const { return this->m_protein_pct ; } -double Fermentable::maxInBatch_pct() const { return this->m_maxInBatch_pct ; } -bool Fermentable::recommendMash() const { return this->m_recommendMash ; } -double Fermentable::ibuGalPerLb() const { return this->m_ibuGalPerLb ; } -bool Fermentable::isMashed() const { return this->m_isMashed ; } +Fermentable::Type Fermentable::type () const { return this->m_type ; } +double Fermentable::amount () const { return this->m_amount ; } +bool Fermentable::amountIsWeight () const { return this->m_amountIsWeight ; } // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ +double Fermentable::yield_pct () const { return this->m_yield_pct ; } +double Fermentable::color_srm () const { return this->m_color_srm ; } +bool Fermentable::addAfterBoil () const { return this->m_addAfterBoil ; } +QString Fermentable::origin () const { return this->m_origin ; } +QString Fermentable::supplier () const { return this->m_supplier ; } +QString Fermentable::notes () const { return this->m_notes ; } +double Fermentable::coarseFineDiff_pct () const { return this->m_coarseFineDiff_pct ; } +double Fermentable::moisture_pct () const { return this->m_moisture_pct ; } +double Fermentable::diastaticPower_lintner () const { return this->m_diastaticPower_lintner ; } +double Fermentable::protein_pct () const { return this->m_protein_pct ; } +double Fermentable::maxInBatch_pct () const { return this->m_maxInBatch_pct ; } +bool Fermentable::recommendMash () const { return this->m_recommendMash ; } +double Fermentable::ibuGalPerLb () const { return this->m_ibuGalPerLb ; } +bool Fermentable::isMashed () const { return this->m_isMashed ; } // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ -std::optional Fermentable::grainGroup () const { return this->m_grainGroup ; } -std::optional Fermentable::grainGroupAsInt () const { return castToOptInt(this->m_grainGroup) ; } -QString Fermentable::producer () const { return this->m_producer ; } -QString Fermentable::productId () const { return this->m_productId ; } -std::optional Fermentable::fineGrindYield_pct () const { return this->m_fineGrindYield_pct ; } -std::optional Fermentable::coarseGrindYield_pct () const { return this->m_coarseGrindYield_pct ; } -std::optional Fermentable::potentialYield_sg () const { return this->m_potentialYield_sg ; } -std::optional Fermentable::alphaAmylase_dextUnits() const { return this->m_alphaAmylase_dextUnits ; } -std::optional Fermentable::kolbachIndex_pct () const { return this->m_kolbachIndex_pct ; } -MassOrVolumeAmt Fermentable::amountWithUnits () const { return MassOrVolumeAmt{this->m_amount, this->m_amountIsWeight ? Measurement::Units::kilograms : Measurement::Units::liters}; } -std::optional Fermentable::hardnessPrpGlassy_pct () const { return this->m_hardnessPrpGlassy_pct ; } -std::optional Fermentable::hardnessPrpHalf_pct () const { return this->m_hardnessPrpHalf_pct ; } -std::optional Fermentable::hardnessPrpMealy_pct () const { return this->m_hardnessPrpMealy_pct ; } -std::optional Fermentable::kernelSizePrpPlump_pct() const { return this->m_kernelSizePrpPlump_pct ; } -std::optional Fermentable::kernelSizePrpThin_pct () const { return this->m_kernelSizePrpThin_pct ; } -std::optional Fermentable::friability_pct () const { return this->m_friability_pct ; } -std::optional Fermentable::di_ph () const { return this->m_di_ph ; } -std::optional Fermentable::viscosity_cP () const { return this->m_viscosity_cP ; } +std::optional Fermentable::grainGroup () const { return this->m_grainGroup ; } +std::optional Fermentable::grainGroupAsInt () const { return castToOptInt(this->m_grainGroup) ; } +QString Fermentable::producer () const { return this->m_producer ; } +QString Fermentable::productId () const { return this->m_productId ; } +std::optional Fermentable::fineGrindYield_pct () const { return this->m_fineGrindYield_pct ; } +std::optional Fermentable::coarseGrindYield_pct () const { return this->m_coarseGrindYield_pct ; } +std::optional Fermentable::potentialYield_sg () const { return this->m_potentialYield_sg ; } +std::optional Fermentable::alphaAmylase_dextUnits () const { return this->m_alphaAmylase_dextUnits ; } +std::optional Fermentable::kolbachIndex_pct () const { return this->m_kolbachIndex_pct ; } +std::optional Fermentable::hardnessPrpGlassy_pct () const { return this->m_hardnessPrpGlassy_pct ; } +std::optional Fermentable::hardnessPrpHalf_pct () const { return this->m_hardnessPrpHalf_pct ; } +std::optional Fermentable::hardnessPrpMealy_pct () const { return this->m_hardnessPrpMealy_pct ; } +std::optional Fermentable::kernelSizePrpPlump_pct () const { return this->m_kernelSizePrpPlump_pct ; } +std::optional Fermentable::kernelSizePrpThin_pct () const { return this->m_kernelSizePrpThin_pct ; } +std::optional Fermentable::friability_pct () const { return this->m_friability_pct ; } +std::optional Fermentable::di_ph () const { return this->m_di_ph ; } +std::optional Fermentable::viscosity_cP () const { return this->m_viscosity_cP ; } +std::optional Fermentable::dmsP () const { return this->m_dmsP ; } +bool Fermentable::dmsPIsMassPerVolume () const { return this->m_dmsPIsMassPerVolume ; } +std::optional Fermentable::fan () const { return this->m_fan ; } +bool Fermentable::fanIsMassPerVolume () const { return this->m_fanIsMassPerVolume ; } +std::optional Fermentable::fermentability_pct () const { return this->m_fermentability_pct ; } +std::optional Fermentable::betaGlucan () const { return this->m_betaGlucan ; } +bool Fermentable::betaGlucanIsMassPerVolume() const { return this->m_betaGlucanIsMassPerVolume; } + +// Combined getters (all added for BeerJSON support) +MassOrVolumeAmt Fermentable::amountWithUnits () const { return MassOrVolumeAmt{this->m_amount, this->m_amountIsWeight ? Measurement::Units::kilograms : Measurement::Units::liters}; } +std::optional Fermentable::dmsPWithUnits () const { return Optional::eitherOr(this->m_dmsP , this->m_dmsPIsMassPerVolume , Measurement::Units::milligramsPerLiter, Measurement::Units::partsPerMillion); } +std::optional Fermentable::fanWithUnits () const { return Optional::eitherOr(this->m_fan , this->m_fanIsMassPerVolume , Measurement::Units::milligramsPerLiter, Measurement::Units::partsPerMillion); } +std::optional Fermentable::betaGlucanWithUnits() const { return Optional::eitherOr(this->m_betaGlucan, this->m_betaGlucanIsMassPerVolume, Measurement::Units::milligramsPerLiter, Measurement::Units::partsPerMillion); } + bool Fermentable::isExtract() const { return ((type() == Fermentable::Type::Extract) || (type() == Fermentable::Type::Dry_Extract)); @@ -346,43 +377,75 @@ double Fermentable::equivSucrose_kg() const { } //============================================= "SETTER" MEMBER FUNCTIONS ============================================== -void Fermentable::setType (Type const val) { this->setAndNotify(PropertyNames::Fermentable::type , this->m_type , val); } -void Fermentable::setAddAfterBoil (bool const val) { this->setAndNotify(PropertyNames::Fermentable::addAfterBoil , this->m_addAfterBoil , val); } -void Fermentable::setOrigin (QString const & val) { this->setAndNotify(PropertyNames::Fermentable::origin , this->m_origin , val); } -void Fermentable::setSupplier (QString const & val) { this->setAndNotify(PropertyNames::Fermentable::supplier , this->m_supplier , val); } -void Fermentable::setNotes (QString const & val) { this->setAndNotify(PropertyNames::Fermentable::notes , this->m_notes , val); } -void Fermentable::setRecommendMash (bool const val) { this->setAndNotify(PropertyNames::Fermentable::recommendMash , this->m_recommendMash , val); } -void Fermentable::setIsMashed (bool const val) { this->setAndNotify(PropertyNames::Fermentable::isMashed , this->m_isMashed , val); } -void Fermentable::setIbuGalPerLb (double const val) { this->setAndNotify(PropertyNames::Fermentable::ibuGalPerLb , this->m_ibuGalPerLb , val); } -void Fermentable::setAmount (double const val) { this->setAndNotify(PropertyNames::Fermentable::amount , this->m_amount , this->enforceMin (val, "amount")); } -void Fermentable::setAmountIsWeight (bool const val) { this->setAndNotify(PropertyNames::Fermentable::amountIsWeight , this->m_amountIsWeight , val); } // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ -void Fermentable::setYield_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::yield_pct , this->m_yield_pct , this->enforceMinAndMax(val, "amount", 0.0, 100.0)); } -void Fermentable::setColor_srm (double const val) { this->setAndNotify(PropertyNames::Fermentable::color_srm , this->m_color_srm , this->enforceMin (val, "color")); } -void Fermentable::setCoarseFineDiff_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::coarseFineDiff_pct , this->m_coarseFineDiff_pct , this->enforceMinAndMax(val, "coarseFineDiff", 0.0, 100.0)); } -void Fermentable::setMoisture_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::moisture_pct , this->m_moisture_pct , this->enforceMinAndMax(val, "moisture", 0.0, 100.0)); } -void Fermentable::setDiastaticPower_lintner(double const val) { this->setAndNotify(PropertyNames::Fermentable::diastaticPower_lintner, this->m_diastaticPower_lintner, this->enforceMin (val, "diastatic power")); } -void Fermentable::setProtein_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::protein_pct , this->m_protein_pct , this->enforceMinAndMax(val, "protein", 0.0, 100.0)); } -void Fermentable::setMaxInBatch_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::maxInBatch_pct , this->m_maxInBatch_pct , this->enforceMinAndMax(val, "max in batch", 0.0, 100.0)); } +void Fermentable::setType (Type const val) { this->setAndNotify(PropertyNames::Fermentable::type , this->m_type , val); } +void Fermentable::setAddAfterBoil (bool const val) { this->setAndNotify(PropertyNames::Fermentable::addAfterBoil , this->m_addAfterBoil , val); } +void Fermentable::setOrigin (QString const & val) { this->setAndNotify(PropertyNames::Fermentable::origin , this->m_origin , val); } +void Fermentable::setSupplier (QString const & val) { this->setAndNotify(PropertyNames::Fermentable::supplier , this->m_supplier , val); } +void Fermentable::setNotes (QString const & val) { this->setAndNotify(PropertyNames::Fermentable::notes , this->m_notes , val); } +void Fermentable::setRecommendMash (bool const val) { this->setAndNotify(PropertyNames::Fermentable::recommendMash , this->m_recommendMash , val); } +void Fermentable::setIsMashed (bool const val) { this->setAndNotify(PropertyNames::Fermentable::isMashed , this->m_isMashed , val); } +void Fermentable::setIbuGalPerLb (double const val) { this->setAndNotify(PropertyNames::Fermentable::ibuGalPerLb , this->m_ibuGalPerLb , val); } +void Fermentable::setAmount (double const val) { this->setAndNotify(PropertyNames::Fermentable::amount , this->m_amount , this->enforceMin (val, "amount")); } +void Fermentable::setAmountIsWeight (bool const val) { this->setAndNotify(PropertyNames::Fermentable::amountIsWeight , this->m_amountIsWeight , val); } // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ +void Fermentable::setYield_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::yield_pct , this->m_yield_pct , this->enforceMinAndMax(val, "amount", 0.0, 100.0)); } +void Fermentable::setColor_srm (double const val) { this->setAndNotify(PropertyNames::Fermentable::color_srm , this->m_color_srm , this->enforceMin (val, "color")); } +void Fermentable::setCoarseFineDiff_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::coarseFineDiff_pct , this->m_coarseFineDiff_pct , this->enforceMinAndMax(val, "coarseFineDiff", 0.0, 100.0)); } +void Fermentable::setMoisture_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::moisture_pct , this->m_moisture_pct , this->enforceMinAndMax(val, "moisture", 0.0, 100.0)); } +void Fermentable::setDiastaticPower_lintner (double const val) { this->setAndNotify(PropertyNames::Fermentable::diastaticPower_lintner , this->m_diastaticPower_lintner , this->enforceMin (val, "diastatic power")); } +void Fermentable::setProtein_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::protein_pct , this->m_protein_pct , this->enforceMinAndMax(val, "protein", 0.0, 100.0)); } +void Fermentable::setMaxInBatch_pct (double const val) { this->setAndNotify(PropertyNames::Fermentable::maxInBatch_pct , this->m_maxInBatch_pct , this->enforceMinAndMax(val, "max in batch", 0.0, 100.0)); } // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ -void Fermentable::setGrainGroup (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::grainGroup , this->m_grainGroup , val ); } -void Fermentable::setGrainGroupAsInt (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::grainGroup , this->m_grainGroup , castFromOptInt(val)); } -void Fermentable::setProducer (QString const & val) { this->setAndNotify(PropertyNames::Fermentable::producer , this->m_producer , val ); } -void Fermentable::setProductId (QString const & val) { this->setAndNotify(PropertyNames::Fermentable::productId , this->m_productId , val ); } -void Fermentable::setFineGrindYield_pct (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::fineGrindYield_pct , this->m_fineGrindYield_pct , val ); } -void Fermentable::setCoarseGrindYield_pct (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::coarseGrindYield_pct , this->m_coarseGrindYield_pct , val ); } -void Fermentable::setPotentialYield_sg (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::potentialYield_sg , this->m_potentialYield_sg , val ); } -void Fermentable::setAlphaAmylase_dextUnits(std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::alphaAmylase_dextUnits, this->m_alphaAmylase_dextUnits, val ); } -void Fermentable::setKolbachIndex_pct (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::kolbachIndex_pct , this->m_kolbachIndex_pct , val ); } -void Fermentable::setAmountWithUnits (MassOrVolumeAmt const val) { this->setAndNotify(PropertyNames::Fermentable::amount , this->m_amount , val.quantity() ); // Continues to next line! - this->setAndNotify(PropertyNames::Fermentable::amountIsWeight , this->m_amountIsWeight , val.isMass() ); } -void Fermentable::setHardnessPrpGlassy_pct (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::hardnessPrpGlassy_pct , this->m_hardnessPrpGlassy_pct , val ); } -void Fermentable::setHardnessPrpHalf_pct (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::hardnessPrpHalf_pct , this->m_hardnessPrpHalf_pct , val ); } -void Fermentable::setHardnessPrpMealy_pct (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::hardnessPrpMealy_pct , this->m_hardnessPrpMealy_pct , val ); } -void Fermentable::setKernelSizePrpPlump_pct(std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::kernelSizePrpPlump_pct, this->m_kernelSizePrpPlump_pct, val ); } -void Fermentable::setKernelSizePrpThin_pct (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::kernelSizePrpThin_pct , this->m_kernelSizePrpThin_pct , val ); } -void Fermentable::setFriability_pct (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::friability_pct , this->m_friability_pct , val ); } -void Fermentable::setDi_ph (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::di_ph , this->m_di_ph , val ); } -void Fermentable::setViscosity_cP (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::viscosity_cP , this->m_viscosity_cP , val ); } +void Fermentable::setGrainGroup (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::grainGroup , this->m_grainGroup , val ); } +void Fermentable::setGrainGroupAsInt (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::grainGroup , this->m_grainGroup , castFromOptInt(val)); } +void Fermentable::setProducer (QString const & val) { this->setAndNotify(PropertyNames::Fermentable::producer , this->m_producer , val ); } +void Fermentable::setProductId (QString const & val) { this->setAndNotify(PropertyNames::Fermentable::productId , this->m_productId , val ); } +void Fermentable::setFineGrindYield_pct (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::fineGrindYield_pct , this->m_fineGrindYield_pct , val ); } +void Fermentable::setCoarseGrindYield_pct (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::coarseGrindYield_pct , this->m_coarseGrindYield_pct , val ); } +void Fermentable::setPotentialYield_sg (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::potentialYield_sg , this->m_potentialYield_sg , val ); } +void Fermentable::setAlphaAmylase_dextUnits (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::alphaAmylase_dextUnits , this->m_alphaAmylase_dextUnits , val ); } +void Fermentable::setKolbachIndex_pct (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::kolbachIndex_pct , this->m_kolbachIndex_pct , val ); } +void Fermentable::setHardnessPrpGlassy_pct (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::hardnessPrpGlassy_pct , this->m_hardnessPrpGlassy_pct , val ); } +void Fermentable::setHardnessPrpHalf_pct (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::hardnessPrpHalf_pct , this->m_hardnessPrpHalf_pct , val ); } +void Fermentable::setHardnessPrpMealy_pct (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::hardnessPrpMealy_pct , this->m_hardnessPrpMealy_pct , val ); } +void Fermentable::setKernelSizePrpPlump_pct (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::kernelSizePrpPlump_pct , this->m_kernelSizePrpPlump_pct , val ); } +void Fermentable::setKernelSizePrpThin_pct (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::kernelSizePrpThin_pct , this->m_kernelSizePrpThin_pct , val ); } +void Fermentable::setFriability_pct (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::friability_pct , this->m_friability_pct , val ); } +void Fermentable::setDi_ph (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::di_ph , this->m_di_ph , val ); } +void Fermentable::setViscosity_cP (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::viscosity_cP , this->m_viscosity_cP , val ); } +void Fermentable::setDmsP (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::dmsP , this->m_dmsP , val ); } +void Fermentable::setDmsPIsMassPerVolume (bool const val) { this->setAndNotify(PropertyNames::Fermentable::dmsPIsMassPerVolume , this->m_dmsPIsMassPerVolume , val ); } +void Fermentable::setFan (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::fan , this->m_fan , val ); } +void Fermentable::setFanIsMassPerVolume (bool const val) { this->setAndNotify(PropertyNames::Fermentable::fanIsMassPerVolume , this->m_fanIsMassPerVolume , val ); } +void Fermentable::setFermentability_pct (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::fermentability_pct , this->m_fermentability_pct , val ); } +void Fermentable::setBetaGlucan (std::optional const val) { this->setAndNotify(PropertyNames::Fermentable::betaGlucan , this->m_betaGlucan , val ); } +void Fermentable::setBetaGlucanIsMassPerVolume(bool const val) { this->setAndNotify(PropertyNames::Fermentable::betaGlucanIsMassPerVolume, this->m_betaGlucanIsMassPerVolume, val ); } + +void Fermentable::setAmountWithUnits (MassOrVolumeAmt const val) { + this->setAndNotify(PropertyNames::Fermentable::amount , this->m_amount , val.quantity()); + this->setAndNotify(PropertyNames::Fermentable::amountIsWeight, this->m_amountIsWeight, val.isMass() ); + return; +} +void Fermentable::setDmsPWithUnits (std::optional const val) { + std::optional quantity = std::nullopt; // Gets set by Optional::eitherOr + bool const isMassPerVolume = Optional::eitherOr(val, quantity); + this->setAndNotify(PropertyNames::Fermentable::dmsP , this->m_dmsP , quantity ); + this->setAndNotify(PropertyNames::Fermentable::dmsPIsMassPerVolume, this->m_dmsPIsMassPerVolume, isMassPerVolume); + return; +} +void Fermentable::setFanWithUnits (std::optional const val) { + std::optional quantity = std::nullopt; // Gets set by Optional::eitherOr + bool const isMassPerVolume = Optional::eitherOr(val, quantity); + this->setAndNotify(PropertyNames::Fermentable::fan , this->m_fan , quantity ); + this->setAndNotify(PropertyNames::Fermentable::fanIsMassPerVolume, this->m_fanIsMassPerVolume, isMassPerVolume); + return; +} +void Fermentable::setBetaGlucanWithUnits(std::optional const val) { + std::optional quantity = std::nullopt; // Gets set by Optional::eitherOr + bool const isMassPerVolume = Optional::eitherOr(val, quantity); + this->setAndNotify(PropertyNames::Fermentable::betaGlucan , this->m_betaGlucan , quantity ); + this->setAndNotify(PropertyNames::Fermentable::betaGlucanIsMassPerVolume, this->m_betaGlucanIsMassPerVolume, isMassPerVolume); + return; +} void Fermentable::setInventoryAmount(double num) { InventoryUtils::setAmount(*this, num); diff --git a/src/model/Fermentable.h b/src/model/Fermentable.h index 1de7572b..c9467181 100644 --- a/src/model/Fermentable.h +++ b/src/model/Fermentable.h @@ -43,40 +43,50 @@ //========================================== Start of property name constants ========================================== // See comment in model/NamedEntity.h #define AddPropertyName(property) namespace PropertyNames::Fermentable { BtStringConst const property{#property}; } -AddPropertyName(addAfterBoil ) -AddPropertyName(alphaAmylase_dextUnits) -AddPropertyName(amount ) -AddPropertyName(amountIsWeight ) -AddPropertyName(amountWithUnits ) -AddPropertyName(coarseFineDiff_pct ) -AddPropertyName(coarseGrindYield_pct ) -AddPropertyName(color_srm ) -AddPropertyName(diastaticPower_lintner) -AddPropertyName(di_ph ) -AddPropertyName(fineGrindYield_pct ) -AddPropertyName(friability_pct ) -AddPropertyName(grainGroup ) -AddPropertyName(hardnessPrpGlassy_pct ) -AddPropertyName(hardnessPrpHalf_pct ) -AddPropertyName(hardnessPrpMealy_pct ) -AddPropertyName(ibuGalPerLb ) -AddPropertyName(isMashed ) -AddPropertyName(kernelSizePrpPlump_pct) -AddPropertyName(kernelSizePrpThin_pct ) -AddPropertyName(kolbachIndex_pct ) -AddPropertyName(maxInBatch_pct ) -AddPropertyName(moisture_pct ) -AddPropertyName(notes ) -AddPropertyName(origin ) -AddPropertyName(potentialYield_sg ) -AddPropertyName(producer ) -AddPropertyName(productId ) -AddPropertyName(protein_pct ) -AddPropertyName(recommendMash ) -AddPropertyName(supplier ) -AddPropertyName(type ) -AddPropertyName(viscosity_cP ) -AddPropertyName(yield_pct ) +AddPropertyName(addAfterBoil ) +AddPropertyName(alphaAmylase_dextUnits ) +AddPropertyName(amount ) +AddPropertyName(amountIsWeight ) +AddPropertyName(amountWithUnits ) +AddPropertyName(betaGlucan ) +AddPropertyName(betaGlucanIsMassPerVolume) +AddPropertyName(betaGlucanWithUnits ) +AddPropertyName(coarseFineDiff_pct ) +AddPropertyName(coarseGrindYield_pct ) +AddPropertyName(color_srm ) +AddPropertyName(diastaticPower_lintner ) +AddPropertyName(di_ph ) +AddPropertyName(dmsP ) +AddPropertyName(dmsPIsMassPerVolume ) +AddPropertyName(dmsPWithUnits ) +AddPropertyName(fan ) +AddPropertyName(fanIsMassPerVolume ) +AddPropertyName(fanWithUnits ) +AddPropertyName(fermentability_pct ) +AddPropertyName(fineGrindYield_pct ) +AddPropertyName(friability_pct ) +AddPropertyName(grainGroup ) +AddPropertyName(hardnessPrpGlassy_pct ) +AddPropertyName(hardnessPrpHalf_pct ) +AddPropertyName(hardnessPrpMealy_pct ) +AddPropertyName(ibuGalPerLb ) +AddPropertyName(isMashed ) +AddPropertyName(kernelSizePrpPlump_pct ) +AddPropertyName(kernelSizePrpThin_pct ) +AddPropertyName(kolbachIndex_pct ) +AddPropertyName(maxInBatch_pct ) +AddPropertyName(moisture_pct ) +AddPropertyName(notes ) +AddPropertyName(origin ) +AddPropertyName(potentialYield_sg ) +AddPropertyName(producer ) +AddPropertyName(productId ) +AddPropertyName(protein_pct ) +AddPropertyName(recommendMash ) +AddPropertyName(supplier ) +AddPropertyName(type ) +AddPropertyName(viscosity_cP ) +AddPropertyName(yield_pct ) #undef AddPropertyName //=========================================== End of property name constants =========================================== //====================================================================================================================== @@ -354,11 +364,6 @@ class Fermentable : public NamedEntityWithInventory { */ Q_PROPERTY(std::optional friability_pct READ friability_pct WRITE setFriability_pct ) -/** - * .:TODO JSON:. Finish adding the other BeerJSON fields to \c Fermentable - * ... - * - */ /** * \brief DI pH is the pH of the resultant wort for 40 grams of grain mashed in 100 mL of distilled water (or 1 lb of * grain mashed in 1 gallon of distilled water). Can be used in water chemistry and/or mash pH prediction @@ -395,54 +400,108 @@ class Fermentable : public NamedEntityWithInventory { * * Measurement of DMS-P is often performed by heating "congress wort" samples (see above) in closed vials, * sampling the headspace after incubation, and quantifying DMS using gas chromatography. + * + * BeerJSON allows this attribute to be specified as either volume concentration (ppm or ppb) or mass + * concentration (mg / L) + * + * You might argue that, strictly speaking, \c dmsPIsMassPerVolume should also be optional and set + * if-and-only-if \c dmsP is set. However, that would be a whole bunch of additional complexity for almost no + * gain. Instead, we say the value of \c dmsPIsMassPerVolume is ignored as meaningless when \c dmsP is not + * set. Same applies for \c fan / \c fanIsMassPerVolume and \c betaGlucan / \c betaGlucanIsMassPerVolume. */ -/// Q_PROPERTY(std::optional dmsP_ppm READ dmsP_ppm WRITE setDmsP_ppm ) - + Q_PROPERTY(std::optional dmsP READ dmsP WRITE setDmsP ) + Q_PROPERTY(bool dmsPIsMassPerVolume READ dmsPIsMassPerVolume WRITE setDmsPIsMassPerVolume) + Q_PROPERTY(std::optional dmsPWithUnits READ dmsPWithUnits WRITE setDmsPWithUnits ) -// dmsP_XXXunitXXX //conc units -//fan //conc units -//fermentability_pct -//beta_glucan //conc units + /** + * \brief Free Amino Nitrogen (FAN) is a measure of the concentration of nitrogen-containing compounds -- including + * amino acids, ammonia and small peptides (one to three units) -- which can be metabolized by yeast for cell + * growth and proliferation. As with some of the other measures here, this is a measure of the wort, so, in + * the context of a fermentable, it is measure of a congress wort produced from this malt. + * + * As a diagnostic test, low FAN measurements indicate slow or incomplete fermentation, while high FAN + * measurements may indicate haze issues and/or diacetyl formation. + * + * BeerJSON allows this attribute to be specified as either volume concentration (ppm or ppb) or mass + * concentration (mg / L) + */ + Q_PROPERTY(std::optional fan READ fan WRITE setFan ) + Q_PROPERTY(bool fanIsMassPerVolume READ fanIsMassPerVolume WRITE setFanIsMassPerVolume) + Q_PROPERTY(std::optional fanWithUnits READ fanWithUnits WRITE setFanWithUnits ) + /** + * \brief Fermentability is used in Extracts to indicate a baseline typical apparent attenuation for a typical medium + * attenuation yeast. + */ + Q_PROPERTY(std::optional fermentability_pct READ fermentability_pct WRITE setFermentability_pct) + /** + * \brief Beta-glucans (β-glucans) are polysaccharides of D-glucose monomers linked by beta-glycosidic bonds. + * β-glucans are present in the cell walls of various cereals and are capable of clogging process filters; so, + * too high a concentration of β-glucans in the wort may cause result in haze in the end product beer. + * + * As with some of the other measures here, the concentration of β-glucans is measured in the wort, so, in the + * context of a fermentable, it is measure of a congress wort produced from this malt. + * + * BeerJSON allows this attribute to be specified as either volume concentration (ppm or ppb) or mass + * concentration (mg / L) + * + * Yes, it would be neat to include β in the property, variable and function names etc, but I think the Qt MOC + * doesn't like it. + */ + Q_PROPERTY(std::optional betaGlucan READ betaGlucan WRITE setBetaGlucan ) + Q_PROPERTY(bool betaGlucanIsMassPerVolume READ betaGlucanIsMassPerVolume WRITE setBetaGlucanIsMassPerVolume) + Q_PROPERTY(std::optional betaGlucanWithUnits READ betaGlucanWithUnits WRITE setBetaGlucanWithUnits ) //============================================ "GETTER" MEMBER FUNCTIONS ============================================ - Type type () const; - double amount () const; - bool amountIsWeight () const; // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ - double yield_pct () const; - double color_srm () const; - bool addAfterBoil () const; - QString origin () const; - QString supplier () const; - QString notes () const; - double coarseFineDiff_pct () const; - double moisture_pct () const; - double diastaticPower_lintner () const; - double protein_pct () const; - double maxInBatch_pct () const; - bool recommendMash () const; - double ibuGalPerLb () const; - bool isMashed () const; + Type type () const; + double amount () const; + bool amountIsWeight () const; // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ + double yield_pct () const; + double color_srm () const; + bool addAfterBoil () const; + QString origin () const; + QString supplier () const; + QString notes () const; + double coarseFineDiff_pct () const; + double moisture_pct () const; + double diastaticPower_lintner () const; + double protein_pct () const; + double maxInBatch_pct () const; + bool recommendMash () const; + double ibuGalPerLb () const; + bool isMashed () const; // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - std::optional grainGroup () const; - std::optional grainGroupAsInt () const; - QString producer () const; - QString productId () const; - std::optional fineGrindYield_pct () const; - std::optional coarseGrindYield_pct () const; - std::optional potentialYield_sg () const; - std::optional alphaAmylase_dextUnits() const; - std::optional kolbachIndex_pct () const; - MassOrVolumeAmt amountWithUnits () const; - std::optional hardnessPrpGlassy_pct () const; - std::optional hardnessPrpHalf_pct () const; - std::optional hardnessPrpMealy_pct () const; - std::optional kernelSizePrpPlump_pct() const; - std::optional kernelSizePrpThin_pct () const; - std::optional friability_pct () const; - std::optional di_ph () const; - std::optional viscosity_cP () const; + std::optional grainGroup () const; + std::optional grainGroupAsInt () const; + QString producer () const; + QString productId () const; + std::optional fineGrindYield_pct () const; + std::optional coarseGrindYield_pct () const; + std::optional potentialYield_sg () const; + std::optional alphaAmylase_dextUnits () const; + std::optional kolbachIndex_pct () const; + std::optional hardnessPrpGlassy_pct () const; + std::optional hardnessPrpHalf_pct () const; + std::optional hardnessPrpMealy_pct () const; + std::optional kernelSizePrpPlump_pct () const; + std::optional kernelSizePrpThin_pct () const; + std::optional friability_pct () const; + std::optional di_ph () const; + std::optional viscosity_cP () const; + std::optional dmsP () const; + bool dmsPIsMassPerVolume () const; + std::optional fan () const; + bool fanIsMassPerVolume () const; + std::optional fermentability_pct () const; + std::optional betaGlucan () const; + bool betaGlucanIsMassPerVolume() const; + + // Combined getters (all added for BeerJSON support) + MassOrVolumeAmt amountWithUnits () const; + std::optional dmsPWithUnits () const; + std::optional fanWithUnits () const; + std::optional betaGlucanWithUnits() const; // Calculated getters. double equivSucrose_kg () const; @@ -452,42 +511,54 @@ class Fermentable : public NamedEntityWithInventory { virtual double inventory() const; //============================================ "SETTER" MEMBER FUNCTIONS ============================================ - void setType (Type const val); - void setAmount (double const val); - void setAmountIsWeight (bool const val); // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ - void setYield_pct (double const val); - void setColor_srm (double const val); - void setAddAfterBoil (bool const val); - void setOrigin (QString const & val); - void setSupplier (QString const & val); - void setNotes (QString const & val); - void setCoarseFineDiff_pct (double const val); - void setMoisture_pct (double const val); - void setDiastaticPower_lintner(double const val); - void setProtein_pct (double const val); - void setMaxInBatch_pct (double const val); - void setRecommendMash (bool const val); - void setIbuGalPerLb (double const val); - void setIsMashed (bool const val); + void setType (Type const val); + void setAmount (double const val); + void setAmountIsWeight (bool const val); // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ + void setYield_pct (double const val); + void setColor_srm (double const val); + void setAddAfterBoil (bool const val); + void setOrigin (QString const & val); + void setSupplier (QString const & val); + void setNotes (QString const & val); + void setCoarseFineDiff_pct (double const val); + void setMoisture_pct (double const val); + void setDiastaticPower_lintner (double const val); + void setProtein_pct (double const val); + void setMaxInBatch_pct (double const val); + void setRecommendMash (bool const val); + void setIbuGalPerLb (double const val); + void setIsMashed (bool const val); // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - void setGrainGroup (std::optional const val); - void setGrainGroupAsInt (std::optional const val); - void setProducer (QString const & val); - void setProductId (QString const & val); - void setFineGrindYield_pct (std::optional const val); - void setCoarseGrindYield_pct (std::optional const val); - void setPotentialYield_sg (std::optional const val); - void setAlphaAmylase_dextUnits(std::optional const val); - void setKolbachIndex_pct (std::optional const val); - void setAmountWithUnits (MassOrVolumeAmt const val); - void setHardnessPrpGlassy_pct (std::optional const val); - void setHardnessPrpHalf_pct (std::optional const val); - void setHardnessPrpMealy_pct (std::optional const val); - void setKernelSizePrpPlump_pct(std::optional const val); - void setKernelSizePrpThin_pct (std::optional const val); - void setFriability_pct (std::optional const val); - void setDi_ph (std::optional const val); - void setViscosity_cP (std::optional const val); + void setGrainGroup (std::optional const val); + void setGrainGroupAsInt (std::optional const val); + void setProducer (QString const & val); + void setProductId (QString const & val); + void setFineGrindYield_pct (std::optional const val); + void setCoarseGrindYield_pct (std::optional const val); + void setPotentialYield_sg (std::optional const val); + void setAlphaAmylase_dextUnits (std::optional const val); + void setKolbachIndex_pct (std::optional const val); + void setHardnessPrpGlassy_pct (std::optional const val); + void setHardnessPrpHalf_pct (std::optional const val); + void setHardnessPrpMealy_pct (std::optional const val); + void setKernelSizePrpPlump_pct (std::optional const val); + void setKernelSizePrpThin_pct (std::optional const val); + void setFriability_pct (std::optional const val); + void setDi_ph (std::optional const val); + void setViscosity_cP (std::optional const val); + void setDmsP (std::optional const val); + void setDmsPIsMassPerVolume (bool const val); + void setFan (std::optional const val); + void setFanIsMassPerVolume (bool const val); + void setFermentability_pct (std::optional const val); + void setBetaGlucan (std::optional const val); + void setBetaGlucanIsMassPerVolume(bool const val); + + // Combined setters (all added for BeerJSON support) + void setAmountWithUnits (MassOrVolumeAmt const val); + void setDmsPWithUnits (std::optional const val); + void setFanWithUnits (std::optional const val); + void setBetaGlucanWithUnits(std::optional const val); virtual void setInventoryAmount(double amount); @@ -500,41 +571,47 @@ class Fermentable : public NamedEntityWithInventory { virtual ObjectStore & getObjectStoreTypedInstance() const; private: - Type m_type ; - double m_amount ; // Primarily valid in "Use Of" instance - bool m_amountIsWeight ; // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ - double m_yield_pct ; - double m_color_srm ; - bool m_addAfterBoil ; // Primarily valid in "Use Of" instance - QString m_origin ; - QString m_supplier ; - QString m_notes ; - double m_coarseFineDiff_pct ; - double m_moisture_pct ; - double m_diastaticPower_lintner; - double m_protein_pct ; - double m_maxInBatch_pct ; - bool m_recommendMash ; - double m_ibuGalPerLb ; - bool m_isMashed ; // Primarily valid in "Use Of" instance + Type m_type ; + double m_amount ; // Primarily valid in "Use Of" instance + bool m_amountIsWeight ; // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ + double m_yield_pct ; + double m_color_srm ; + bool m_addAfterBoil ; // Primarily valid in "Use Of" instance + QString m_origin ; + QString m_supplier ; + QString m_notes ; + double m_coarseFineDiff_pct ; + double m_moisture_pct ; + double m_diastaticPower_lintner ; + double m_protein_pct ; + double m_maxInBatch_pct ; + bool m_recommendMash ; + double m_ibuGalPerLb ; + bool m_isMashed ; // Primarily valid in "Use Of" instance // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - std::optional m_grainGroup ; - QString m_producer ; - QString m_productId ; - std::optional m_fineGrindYield_pct ; - std::optional m_coarseGrindYield_pct ; - std::optional m_potentialYield_sg ; - std::optional m_alphaAmylase_dextUnits; - std::optional m_kolbachIndex_pct ; - std::optional m_hardnessPrpGlassy_pct ; - std::optional m_hardnessPrpHalf_pct ; - std::optional m_hardnessPrpMealy_pct ; - std::optional m_kernelSizePrpPlump_pct; - std::optional m_kernelSizePrpThin_pct ; - std::optional m_friability_pct ; - std::optional m_di_ph ; - std::optional m_viscosity_cP ; - + std::optional m_grainGroup ; + QString m_producer ; + QString m_productId ; + std::optional m_fineGrindYield_pct ; + std::optional m_coarseGrindYield_pct ; + std::optional m_potentialYield_sg ; + std::optional m_alphaAmylase_dextUnits ; + std::optional m_kolbachIndex_pct ; + std::optional m_hardnessPrpGlassy_pct ; + std::optional m_hardnessPrpHalf_pct ; + std::optional m_hardnessPrpMealy_pct ; + std::optional m_kernelSizePrpPlump_pct ; + std::optional m_kernelSizePrpThin_pct ; + std::optional m_friability_pct ; + std::optional m_di_ph ; + std::optional m_viscosity_cP ; + std::optional m_dmsP ; + bool m_dmsPIsMassPerVolume ; + std::optional m_fan ; + bool m_fanIsMassPerVolume ; + std::optional m_fermentability_pct ; + std::optional m_betaGlucan ; + bool m_betaGlucanIsMassPerVolume; }; Q_DECLARE_METATYPE(QList) diff --git a/src/model/NamedEntity.cpp b/src/model/NamedEntity.cpp index c57a4d77..ea4d79fe 100644 --- a/src/model/NamedEntity.cpp +++ b/src/model/NamedEntity.cpp @@ -25,6 +25,7 @@ #include #include "database/ObjectStore.h" +#include "measurement/ConstrainedAmount.h" #include "model/NamedParameterBundle.h" #include "model/Recipe.h" @@ -340,6 +341,99 @@ QMetaProperty NamedEntity::metaProperty(char const * const name) const { return this->metaObject()->property(this->metaObject()->indexOfProperty(name)); } +template +void NamedEntity::setEitherOrReqParams(NamedParameterBundle const & namedParameterBundle, + BtStringConst const & quantityParameterName, + BtStringConst const & isFirstUnitParameterName, + BtStringConst const & combinedWithUnitsParameterName, + double & quantityReturn, + bool & isFirstUnitReturn) { + if (namedParameterBundle.contains(quantityParameterName)) { + quantityReturn = namedParameterBundle.val(quantityParameterName ); + isFirstUnitReturn = namedParameterBundle.val(isFirstUnitParameterName); + } else { + auto const combinedWithUnits = namedParameterBundle.val(combinedWithUnitsParameterName); + // It is the caller's responsibility to have converted to canonical units -- ie a coding error if this did not + // happen. Asserting without the diagnostic info is not much use, so we do the check first, then the assert. + auto const * suppliedUnit = combinedWithUnits.unit(); + if (!suppliedUnit->isCanonical()) { + qCritical() << + Q_FUNC_INFO << this->name() << "CODING ERROR:" << combinedWithUnitsParameterName << "supplied in" << + suppliedUnit << "instead of" << suppliedUnit->getCanonical(); + Q_ASSERT(false); + } else { + quantityReturn = combinedWithUnits.quantity(); + isFirstUnitReturn = combinedWithUnits.isFirst(); + } + } + return; +} +// Instantiate the above template function 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 NamedEntity::setEitherOrReqParams(NamedParameterBundle const & namedParameterBundle, + BtStringConst const & quantityParameterName, + BtStringConst const & isFirstUnitParameterName, + BtStringConst const & combinedWithUnitsParameterName, + double & quantityReturn, + bool & isFirstUnitReturn); +template void NamedEntity::setEitherOrReqParams(NamedParameterBundle const & namedParameterBundle, + BtStringConst const & quantityParameterName, + BtStringConst const & isFirstUnitParameterName, + BtStringConst const & combinedWithUnitsParameterName, + double & quantityReturn, + bool & isFirstUnitReturn); + +template +void NamedEntity::setEitherOrOptParams(NamedParameterBundle const & namedParameterBundle, + BtStringConst const & quantityParameterName, + BtStringConst const & isFirstUnitParameterName, + BtStringConst const & combinedWithUnitsParameterName, + std::optional & quantityReturn, + bool & isFirstUnitReturn) { + if (namedParameterBundle.contains(quantityParameterName)) { + quantityReturn = namedParameterBundle.val>(quantityParameterName ); + isFirstUnitReturn = namedParameterBundle.val(isFirstUnitParameterName); + return; + } + + auto const combinedWithUnits = namedParameterBundle.val>(combinedWithUnitsParameterName); + if (!combinedWithUnits) { + // Strictly the isFirstUnitReturn is meaningless / ignored in this case, but we use true as the "default" value + // by convention. (Other than increased complexity, having it std::optional wouldn't buy us much.) + quantityReturn = std::nullopt; + isFirstUnitReturn = true; + return; + } + + // It is the caller's responsibility to have converted to canonical units -- ie a coding error if this did not + // happen. Asserting without the diagnostic info is not much use, so we do the check first, then the assert. + auto const * suppliedUnit = combinedWithUnits->unit(); + if (!suppliedUnit->isCanonical()) { + qCritical() << + Q_FUNC_INFO << this->name() << "CODING ERROR:" << combinedWithUnitsParameterName << "supplied in" << + suppliedUnit << "instead of" << suppliedUnit->getCanonical(); + Q_ASSERT(false); + } + quantityReturn = combinedWithUnits->quantity(); + isFirstUnitReturn = combinedWithUnits->isFirst(); + + return; +} +// Instantiate the above template function for the types that are going to use them +template void NamedEntity::setEitherOrOptParams(NamedParameterBundle const & namedParameterBundle, + BtStringConst const & quantityParameterName, + BtStringConst const & isFirstUnitParameterName, + BtStringConst const & combinedWithUnitsParameterName, + std::optional & quantityReturn, + bool & isFirstUnitReturn); +template void NamedEntity::setEitherOrOptParams(NamedParameterBundle const & namedParameterBundle, + BtStringConst const & quantityParameterName, + BtStringConst const & isFirstUnitParameterName, + BtStringConst const & combinedWithUnitsParameterName, + std::optional & quantityReturn, + bool & isFirstUnitReturn); + + void NamedEntity::prepareForPropertyChange(BtStringConst const & propertyName) { // // At the moment, the only thing we want to do in this pre-change check is to see whether we need to version a diff --git a/src/model/NamedEntity.h b/src/model/NamedEntity.h index df1c03d5..8952594b 100644 --- a/src/model/NamedEntity.h +++ b/src/model/NamedEntity.h @@ -380,6 +380,49 @@ class NamedEntity : public QObject { */ virtual ObjectStore & getObjectStoreTypedInstance() const = 0; + // Depending on who created the bundle, the "either-or" amounts can be set either via their individual properties (eg + // if we're reading from the database) or via their composite attributes (eg if we're reading from a BeerJSON file). + + /** + * \brief Some "either-or" attributes can be measured and stored in two ways, eg the amount of a \c Fermentable can + * be measured either by \c Mass or by \c Volume (typically depending on what type of fermentable it is). In + * these cases, our internal storage is two fields, a \c double measuring the quantity and a \c bool + * indicating whether it is the "first" or the "second" type. Eg for something that's either \c Mass or + * \c Volume, we always have \c Mass as the "first" type and \c Volume as the "second". This is formalised + * somewhat in instances of \c Measurement::ConstrainedAmount (specifically \c MassOrVolumeAmt + * and \c MassOrVolumeConcentrationAmt). + * + * When constructing an object from a \c NamedParameterBundle, there are two ways that an "either-or" + * attribute can be specified. If we're reading from the database, or from BeerXML, then each of the + * underlying fields will be specified individually. Eg, \c PropertyNames::Fermentable::amount and + * \c PropertyNames::Fermentable::amountIsWeight will each be specified in the bundle. However, if we're + * reading from BeerJSON, we'll get a combined quantity-and-units parameter, + * eg \c PropertyNames::Fermentable::amountWithUnits. + * + * This templated function does the generic work for initialising such either-or parameters from a + * \c NamedParameterBundle. + * + * Valid instantiations of this template are with \c MassOrVolumeAmt and \c MassOrVolumeConcentrationAmt. + */ + template + void setEitherOrReqParams(NamedParameterBundle const & namedParameterBundle, + BtStringConst const & quantityParameterName, + BtStringConst const & isFirstUnitParameterName, + BtStringConst const & combinedWithUnitsParameterName, + double & quantityReturn, + bool & isFirstUnitReturn); + + /** + * \brief As \c setEitherOrReqParams but for when the attribute is optional + */ + template + void setEitherOrOptParams(NamedParameterBundle const & namedParameterBundle, + BtStringConst const & quantityParameterName, + BtStringConst const & isFirstUnitParameterName, + BtStringConst const & combinedWithUnitsParameterName, + std::optional & quantityReturn, + bool & isFirstUnitReturn); + /** * \brief Used by setters to force a value not to be below a certain amount * diff --git a/src/model/NamedParameterBundle.cpp b/src/model/NamedParameterBundle.cpp index edf941f0..2ab01455 100644 --- a/src/model/NamedParameterBundle.cpp +++ b/src/model/NamedParameterBundle.cpp @@ -38,6 +38,10 @@ NamedParameterBundle::iterator NamedParameterBundle::insert(BtStringConst const return this->QHash::insert(QString{*parameterName}, value); } +bool NamedParameterBundle::contains(BtStringConst const & parameterName) const { + return this->QHash::contains(*parameterName); +} + QVariant NamedParameterBundle::get(BtStringConst const & parameterName) const { if (!this->contains(*parameterName)) { diff --git a/src/model/NamedParameterBundle.h b/src/model/NamedParameterBundle.h index f466b2a5..bc1dbc2d 100644 --- a/src/model/NamedParameterBundle.h +++ b/src/model/NamedParameterBundle.h @@ -58,9 +58,14 @@ Q_DECLARE_METATYPE(std::optional) Q_DECLARE_METATYPE(std::optional) Q_DECLARE_METATYPE(std::optional) -// Need these to be able to use MassOrVolumeAmt in Qt Properties system +// Need these to be able to use MassOrVolumeAmt and MassOrVolumeConcentrationAmt in Qt Properties system Q_DECLARE_METATYPE(MassOrVolumeAmt ) Q_DECLARE_METATYPE(std::optional) +Q_DECLARE_METATYPE(MassOrVolumeConcentrationAmt ) +Q_DECLARE_METATYPE(std::optional) + + + /** * \brief This allows constructors to be called without a long list of positional parameters and, more importantly, for @@ -81,6 +86,13 @@ class NamedParameterBundle : public QHash { */ QHash::iterator insert(BtStringConst const & parameterName, QVariant const & value); + using QHash::contains; + + /** + * \brief Overload of QHash::contains to support passing \c BtStringConst + */ + bool contains(BtStringConst const & parameterName) const; + /** * \brief Get the value of a parameter that is required to be present in the DB. In "strict" mode, throw an * exception if it is not present. Otherwise, return whatever default value QVariant gives us. diff --git a/src/utils/OptionalHelpers.h b/src/utils/OptionalHelpers.h index b820c266..8041bdff 100644 --- a/src/utils/OptionalHelpers.h +++ b/src/utils/OptionalHelpers.h @@ -93,6 +93,51 @@ namespace Optional { return true; } + /** + * \brief Create an \c std::optional wrapped type \c T (eg \c MassOrVolumeAmt or \c MassOrVolumeConcentrationAmt) + * from an optional \c double and a flag that chooses between two possibilities for the second parameter (of + * type \c U) to construct a \c T. \c U is typically \c Measurement::Unit. + * + * In a lot of model objects, where we allow an optional amount to be measured two ways -- eg by Mass or by + * Volume -- the underlying storage has two fields: an optional double (for the quantity if it's set) and a + * boolean flag (to say which way is being measured -- eg whether the quantity is a Mass or a Volume). We + * sometimes need a single getter to be able to return an optional \c Measurement::ConstrainedAmount derived + * from the two underlying fields. + */ + template + std::optional eitherOr(std::optional const & quantity, + bool const isFirstUnit, + U const & firstUnit, + U const & secondUnit) { + if (!quantity) { + return std::nullopt; + } + return std::make_optional(*quantity, isFirstUnit ? firstUnit : secondUnit); + } + + /** + * \brief This is the inverse of the other \c eitherOr! + * + * Note that the template here does not need to know about the \c Measurement::Unit type. It suffices that + * type \c T (typically \c MassOrVolumeAmt or \c MassOrVolumeConcentrationAmt) implements member functions + * \c quantity() and \c isFirst(). + * + * \param constrainedAmount Input + * \param quantity Output (along with return value) + * + * \return isFirstUnit + */ + template + bool eitherOr(std::optional const & constrainedAmount, std::optional & quantity) { + if (!constrainedAmount) { + quantity = std::nullopt; + // The return value is not really meaningful here, but by convention the default is \c true + return true; + } + quantity = constrainedAmount->quantity(); + return constrainedAmount->isFirst(); + } + } /** diff --git a/src/utils/TypeLookup.h b/src/utils/TypeLookup.h index 09745907..aba51341 100644 --- a/src/utils/TypeLookup.h +++ b/src/utils/TypeLookup.h @@ -230,6 +230,22 @@ class TypeLookup { */ #define PROPERTY_TYPE_LOOKUP_ENTRY(propNameConstVar, memberVar, ...) {&propNameConstVar, TypeInfo::construct(__VA_OPT__ (__VA_ARGS__))} +/** + * \brief Similar to \c PROPERTY_TYPE_LOOKUP_ENTRY but used when we do not have a member variable and instead must use + * the return value of a getter member function. This is usually when we have some combo getters/setters that + * exist primarily for the benefit of BeerJSON. Eg, The \c Fermentable::betaGlucanWithUnits member function + * combines \c Fermentable::m_betaGlucan and \c Fermentable::betaGlucanIsMassPerVolume into a + * \c std::optional return value, so, in \c Fermentable::typeLookup, we include the + * following: + * + * PROPERTY_TYPE_LOOKUP_ENTRY_NO_MV(PropertyNames::Fermentable::betaGlucanWithUnits, Fermentable::betaGlucanWithUnits, Measurement::PqEitherMassOrVolumeConcentration), + * + * Note that, although very similar to \c PROPERTY_TYPE_LOOKUP_ENTRY, the macro is slightly different. We need + * an & in front of MyClass::memberFunction to pass it to decltype (to get the type of the return type of that + * function), whereas we can pass MyClass::memberVariable in direct. + */ +#define PROPERTY_TYPE_LOOKUP_ENTRY_NO_MV(propNameConstVar, getterFunction, ...) {&propNameConstVar, TypeInfo::construct< decltype(&getterFunction)>(__VA_OPT__ (__VA_ARGS__))} + /** * \brief Convenience function for logging */ diff --git a/src/widgets/SmartDigitWidget.cpp b/src/widgets/SmartDigitWidget.cpp index 66aeb533..f1b60c67 100644 --- a/src/widgets/SmartDigitWidget.cpp +++ b/src/widgets/SmartDigitWidget.cpp @@ -38,7 +38,7 @@ class SmartDigitWidget::impl { * Constructor */ impl(SmartDigitWidget & self) : - self {self}, + m_self {self}, m_rgblow {0x0000d0}, m_rgbgood {0x008000}, m_rgbhigh {0xd00000}, @@ -46,14 +46,12 @@ class SmartDigitWidget::impl { m_highLim {1.0}, m_styleSheet {QString("QLabel { font-weight: bold; color: #%1 }")}, m_constantColor{false}, - m_lastNum {1.5}, - m_lastPrec {3}, m_low_msg {SmartDigitWidget::tr("Too low for style.")}, m_good_msg {SmartDigitWidget::tr("In range for style.")}, m_high_msg {SmartDigitWidget::tr("Too high for style.")} { - this->self.setStyleSheet(m_styleSheet.arg(0,6,16,QChar('0'))); - this->self.setFrameStyle(QFrame::Box); - this->self.setFrameShadow(QFrame::Sunken); + this->m_self.setStyleSheet(m_styleSheet.arg(0,6,16,QChar('0'))); + this->m_self.setFrameStyle(QFrame::Box); + this->m_self.setFrameShadow(QFrame::Sunken); return; } @@ -62,50 +60,49 @@ class SmartDigitWidget::impl { */ ~impl() = default; - void setTextStyleAndToolTip(QString str) { + void updateColors() { + // If we don't hold NonPhysicalQuantity, then we need to ensure we're using canonical units to do the limit + // comparisons. + double const displayedValueAsCanonical{ + std::holds_alternative(this->m_self.getFieldType()) ? + this->m_self.getNonOptValueAs() : this->m_self.toCanonical().quantity() + }; + QString style{this->m_styleSheet}; - if ((!this->m_constantColor && (this->m_lastNum < this->m_lowLim)) || - (this->m_constantColor && this->m_color == LOW)) { + if ((!this->m_constantColor && (displayedValueAsCanonical < this->m_lowLim)) || + (this->m_constantColor && this->m_color == SmartDigitWidget::ColorType::Low)) { style = this->m_styleSheet.arg(this->m_rgblow, 6, 16, QChar('0')); - self.setToolTip(this->m_constantColor ? "" : this->m_low_msg); - } else if ((!this->m_constantColor && (this->m_lastNum <= this->m_highLim)) || - (this->m_constantColor && this->m_color == GOOD)) { + m_self.setToolTip(this->m_constantColor ? "" : this->m_low_msg); + } else if ((!this->m_constantColor && (displayedValueAsCanonical <= this->m_highLim)) || + (this->m_constantColor && this->m_color == SmartDigitWidget::ColorType::Good)) { style = this->m_styleSheet.arg(this->m_rgbgood, 6, 16, QChar('0')); - self.setToolTip(this->m_constantColor ? "" : this->m_good_msg); + m_self.setToolTip(this->m_constantColor ? "" : this->m_good_msg); } else { - if (this->m_constantColor && this->m_color == BLACK) { + if (this->m_constantColor && this->m_color == SmartDigitWidget::ColorType::Black) { style = this->m_styleSheet.arg(0, 6, 16, QChar('0')); } else { style = this->m_styleSheet.arg(this->m_rgbhigh, 6, 16, QChar('0')); - self.setToolTip(this->m_high_msg); + m_self.setToolTip(this->m_high_msg); } } - this->self.setStyleSheet(style); - this->self.QLabel::setText(str); - return; - } - - void adjustColors() { - this->setTextStyleAndToolTip(Measurement::displayQuantity(this->m_lastNum, this->m_lastPrec)); + this->m_self.setStyleSheet(style); return; } // Member variables for impl - SmartDigitWidget & self; - unsigned int m_rgblow; - unsigned int m_rgbgood; - unsigned int m_rgbhigh; - double m_lowLim; - double m_highLim; - QString m_styleSheet; - bool m_constantColor; - ColorType m_color; - double m_lastNum; - int m_lastPrec; - QString m_low_msg; - QString m_good_msg; - QString m_high_msg; + SmartDigitWidget & m_self; + unsigned int m_rgblow; + unsigned int m_rgbgood; + unsigned int m_rgbhigh; + double m_lowLim; + double m_highLim; + QString m_styleSheet; + bool m_constantColor; + SmartDigitWidget::ColorType m_color; + QString m_low_msg; + QString m_good_msg; + QString m_high_msg; }; SmartDigitWidget::SmartDigitWidget(QWidget *parent) : @@ -123,6 +120,7 @@ QString SmartDigitWidget::getRawText() const { void SmartDigitWidget::setRawText(QString const & text) { this->QLabel::setText(text); + this->pimpl->updateColors(); return; } @@ -135,33 +133,11 @@ void SmartDigitWidget::doPostInitWork() { return; } -void SmartDigitWidget::display(QString str) { - static bool converted; - - this->pimpl->m_lastNum = Localization::toDouble(str, &converted); - this->pimpl->m_lastPrec = str.length() - str.lastIndexOf(Localization::getLocale().decimalPoint()) - 1; - if (converted) { - this->display(this->pimpl->m_lastNum, this->pimpl->m_lastPrec); - } else { - qWarning() << Q_FUNC_INFO << "Could not convert" << str << "to double"; - QLabel::setText("-"); - } - return; -} - -void SmartDigitWidget::display(double num, int prec) { - this->pimpl->m_lastNum = num; - this->pimpl->m_lastPrec = prec; - - this->pimpl->setTextStyleAndToolTip(QString("%L1").arg(num,0,'f',prec)); - return; -} - void SmartDigitWidget::setLowLim(double num) { if (num < this->pimpl->m_highLim) { this->pimpl->m_lowLim = num; } - this->display(this->pimpl->m_lastNum, this->pimpl->m_lastPrec); + this->pimpl->updateColors(); return; } @@ -169,12 +145,12 @@ void SmartDigitWidget::setHighLim(double num) { if (num > this->pimpl->m_lowLim) { this->pimpl->m_highLim = num; } - this->display(this->pimpl->m_lastNum, this->pimpl->m_lastPrec); + this->pimpl->updateColors(); return; } void SmartDigitWidget::setConstantColor(ColorType c) { - this->pimpl->m_constantColor = (c == LOW || c == GOOD || c == HIGH || c == BLACK ); + this->pimpl->m_constantColor = (c != SmartDigitWidget::ColorType::None); this->pimpl->m_color = c; this->update(); // repaint. return; @@ -185,7 +161,7 @@ void SmartDigitWidget::setLimits(double low, double high) { this->pimpl->m_lowLim = low; this->pimpl->m_highLim = high; } - this->pimpl->adjustColors(); + this->pimpl->updateColors(); this->update(); // repaint. return; } @@ -194,61 +170,14 @@ void SmartDigitWidget::setLowMsg (QString msg) { this->pimpl->m_low_msg = msg; void SmartDigitWidget::setGoodMsg(QString msg) { this->pimpl->m_good_msg = msg; this->update(); return; } void SmartDigitWidget::setHighMsg(QString msg) { this->pimpl->m_high_msg = msg; this->update(); return; } -void SmartDigitWidget::setMessages( QStringList msgs ) { - if ( msgs.size() != 3 ) { - qWarning() << Q_FUNC_INFO << "Wrong number of messages"; - return; - } - this->pimpl->m_low_msg = msgs[0]; - this->pimpl->m_good_msg = msgs[1]; - this->pimpl->m_high_msg = msgs[2]; - - this->pimpl->adjustColors(); - return; -} - -void SmartDigitWidget::setText(QString amount, int precision) { - if (NonPhysicalQuantity::String != std::get(this->fieldType)) { - bool ok = false; - double amt = Measurement::extractRawFromString(amount, &ok); - if (!ok) { - qWarning() << Q_FUNC_INFO << "Could not convert" << amount << "to double"; - } - this->pimpl->m_lastNum = amt; - this->pimpl->m_lastPrec = precision; - this->setText(amt, precision); - return; - } - - this->QLabel::setText(amount); +void SmartDigitWidget::setMessages(QString lowMsg, QString goodMsg, QString highMsg) { + this->pimpl->m_low_msg = lowMsg ; + this->pimpl->m_good_msg = goodMsg; + this->pimpl->m_high_msg = highMsg; + this->pimpl->updateColors(); return; } -void SmartDigitWidget::setText(double amount, int precision) { - this->pimpl->m_lastNum = amount; - this->pimpl->m_lastPrec = precision; -// this->setConfigSection(""); - QLabel::setText(Measurement::displayQuantity(amount, precision)); - return; -} - -template T SmartDigitWidget::getValueAs() const { - return Measurement::extractRawFromString(this->text()); -} -// -// Instantiate the above template function 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.) -// -template int SmartDigitWidget::getValueAs() const; -template unsigned int SmartDigitWidget::getValueAs() const; -template double SmartDigitWidget::getValueAs() const; - -int SmartDigitWidget::getPrecision() const { - return this->pimpl->m_lastPrec; -} - - void SmartDigitWidget::displayChanged(SmartAmounts::ScaleInfo previousScaleInfo) { this->correctEnteredText(previousScaleInfo); return; diff --git a/src/widgets/SmartDigitWidget.h b/src/widgets/SmartDigitWidget.h index a77a0769..77cae86c 100644 --- a/src/widgets/SmartDigitWidget.h +++ b/src/widgets/SmartDigitWidget.h @@ -48,7 +48,7 @@ class SmartDigitWidget : public QLabel, public SmartField { Q_OBJECT public: - enum ColorType{NONE, LOW, GOOD, HIGH, BLACK}; + enum class ColorType{None, Low, Good, High, Black}; SmartDigitWidget(QWidget * parent); virtual ~SmartDigitWidget(); @@ -58,16 +58,16 @@ class SmartDigitWidget : public QLabel, public SmartField { virtual void connectSmartLabelSignal(SmartLabel & smartLabel); virtual void doPostInitWork(); - //! \brief Displays the given \c num with precision \c prec. - void display(double num, int prec = 0); - - //! \brief Display a QString. - void display(QString str); - - //! \brief Set the lower limit of the "good" range. + /** + * \brief Set the lower limit of the "good" range. NB: If we are displaying a \c PhysicalQuantity then num must be + * in canonical units. + */ void setLowLim(double num); - //! \brief Set the upper limit of the "good" range. + /** + * \brief Set the upper limit of the "good" range. NB: If we are displaying a \c PhysicalQuantity then num must be + * in canonical units. + */ void setHighLim(double num); /** @@ -83,17 +83,8 @@ class SmartDigitWidget : public QLabel, public SmartField { void setGoodMsg(QString msg); void setHighMsg(QString msg); - //! \brief the array needs to be low, good, high - void setMessages(QStringList msgs); - - void setText(QString amount, int precision = 2); - void setText(double amount, int precision = 2); - - /** - * \brief Use this when you want to get the text as a number (and ignore any units or other trailling letters or - * symbols) - */ - template T getValueAs() const; + //! \brief Set all the messages + void setMessages(QString lowMsg, QString goodMsg, QString highMsg); public slots: /** @@ -103,20 +94,10 @@ public slots: */ void displayChanged(SmartAmounts::ScaleInfo previousScaleInfo); -protected: - int getPrecision() const; - - BtFieldType fieldType; - private: // Private implementation details - see https://herbsutter.com/gotw/_100/ class impl; std::unique_ptr pimpl; }; -// -// See comment in widgets/BtAmountDigitWidget.h for why we need these trivial child classes to use in .ui files -// -class BtGenericDigit : public SmartDigitWidget { Q_OBJECT public: BtGenericDigit(QWidget * parent); }; - #endif diff --git a/src/widgets/SmartField.cpp b/src/widgets/SmartField.cpp index 4a9a1606..08877a8c 100644 --- a/src/widgets/SmartField.cpp +++ b/src/widgets/SmartField.cpp @@ -196,6 +196,26 @@ class SmartField::impl { return amount; } + /** + * \brief Use this when you want to do something with the returned QString + * + * \param amount Must be in canonical units eg kilograms for mass, liters for volume + */ + [[nodiscard]] QString displayAmount(double amount) const { + // It's a coding error to call this for NonPhysicalQuantity + Q_ASSERT(!std::holds_alternative(*this->m_typeInfo->fieldType)); + + // I find this a nice level of abstraction. This lets all of the setText() + // methods make a single call w/o having to do the logic for finding the + // unit and scale. + return Measurement::displayAmount( + Measurement::Amount{amount, Measurement::Unit::getCanonicalUnit(*this->m_currentPhysicalQuantity)}, + this->m_precision, + this->m_self.getForcedSystemOfMeasurement(), + this->m_self.getForcedRelativeScale() + ); + } + SmartField & m_self; bool m_initialised; char const * m_editorName; @@ -396,7 +416,7 @@ template void SmartField::setAmount(std::optional amoun Q_FUNC_INFO << this->pimpl->m_fieldFqName << ": Trying to set wrong type; m_typeInfo=" << this->pimpl->m_typeInfo << ", typeid(T)=" << typeid(T).name(); Q_ASSERT(false); - } + } if (!amount) { this->setRawText(""); @@ -442,7 +462,7 @@ template void SmartField::setAmount(T amount) { Q_FUNC_INFO << this->pimpl->m_fieldFqName << "forcedSystemOfMeasurement:" << this->getForcedSystemOfMeasurement() << ", forcedRelativeScale:" << this->getForcedRelativeScale(); - this->setRawText(this->displayAmount(amount)); + this->setRawText(this->pimpl->displayAmount(amount)); } return; @@ -454,9 +474,18 @@ template void SmartField::setAmount(T amount) { template void SmartField::setAmount(std::optional amount); template void SmartField::setAmount(std::optional amount); template void SmartField::setAmount(std::optional amount); -template void SmartField::setAmount(int amount); -template void SmartField::setAmount(unsigned int amount); -template void SmartField::setAmount(double amount); +template void SmartField::setAmount(int amount); +template void SmartField::setAmount(unsigned int amount); +template void SmartField::setAmount(double amount); + +void SmartField::setPrecision(unsigned int const precision) { + this->pimpl->m_precision = precision; + return; +} + +[[nodiscard]] unsigned int SmartField::getPrecision() const { + return this->pimpl->m_precision; +} // We can't do the same trick on get-value-as as we do for set-amount because we can't overload base on return type, // hence two different function names. @@ -538,21 +567,6 @@ void SmartField::selectPhysicalQuantity(Measurement::PhysicalQuantity const phys return; } -QString SmartField::displayAmount(double amount) const { - // It's a coding error to call this for NonPhysicalQuantity - Q_ASSERT(!std::holds_alternative(*this->pimpl->m_typeInfo->fieldType)); - - // I find this a nice level of abstraction. This lets all of the setText() - // methods make a single call w/o having to do the logic for finding the - // unit and scale. - return Measurement::displayAmount( - Measurement::Amount{amount, Measurement::Unit::getCanonicalUnit(*this->pimpl->m_currentPhysicalQuantity)}, - this->pimpl->m_precision, - this->getForcedSystemOfMeasurement(), - this->getForcedRelativeScale() - ); -} - void SmartField::correctEnteredText(SmartAmounts::ScaleInfo previousScaleInfo) { Q_ASSERT(this->pimpl->m_initialised); @@ -571,7 +585,7 @@ void SmartField::correctEnteredText(SmartAmounts::ScaleInfo previousScaleInfo) { // amount (aka to SI) and then into the unit we want. Measurement::Amount amountAsCanonical = this->pimpl->toCanonical(enteredText, previousScaleInfo); - QString const correctedText = this->displayAmount(amountAsCanonical.quantity()); + QString const correctedText = this->pimpl->displayAmount(amountAsCanonical.quantity()); qDebug() << Q_FUNC_INFO << this->getFqFieldName() << "Interpreted" << enteredText << "as" << amountAsCanonical << "and corrected to" << correctedText; diff --git a/src/widgets/SmartField.h b/src/widgets/SmartField.h index fc0ebf60..b8375bf2 100644 --- a/src/widgets/SmartField.h +++ b/src/widgets/SmartField.h @@ -119,7 +119,7 @@ class SmartField { * \param precision For a decimal field, this determines the number of decimal places to show. If not specified, we * show 3 decimal places. TBD: IDK if one day we might need to be more sophisticated about this, ie * with number of decimal places dependent on the units that the user has chosen, but for now we - * assume it's the same for everything. + * assume it's the same for everything, but allow modification via \c setPrecision. * * \param maximalDisplayString Used for determining the width of the widget (because a fixed pixel width isn't great * in a world where there are varying display DPIs). @@ -217,6 +217,14 @@ class SmartField { */ template::value> > void setAmount(T amount); + /** + * \brief Normally, you set precision once when \c init is called via \c SMART_FIELD_INIT or similar. However, if + * you really want to modify it on the fly, eg to have different precision for different units, this is what + * you call. Note that you should call this before calling \c setAmount. + */ + void setPrecision(unsigned int const precision); + [[nodiscard]] unsigned int getPrecision() const; + void setForcedSystemOfMeasurement(std::optional systemOfMeasurement); void setForcedRelativeScale(std::optional relativeScale); std::optional getForcedSystemOfMeasurement() const; @@ -267,13 +275,6 @@ class SmartField { */ void selectPhysicalQuantity(Measurement::PhysicalQuantity const physicalQuantity); - /** - * \brief Use this when you want to do something with the returned QString - * - * \param amount Must be in canonical units eg kilograms for mass, liters for volume - */ - [[nodiscard]] QString displayAmount(double amount) const; - /** * \brief When the user has finished entering some text, this function does the corrections, eg if the field is set * to show US Customary volumes and user enters an amount in liters (aka litres) then we need to convert it to diff --git a/src/widgets/SmartLineEdit.cpp b/src/widgets/SmartLineEdit.cpp index 95f65007..dd1c934b 100644 --- a/src/widgets/SmartLineEdit.cpp +++ b/src/widgets/SmartLineEdit.cpp @@ -115,9 +115,7 @@ SmartLineEdit::SmartLineEdit(QWidget * parent) : QLineEdit(parent), SmartField{}, pimpl{std::make_unique(*this)} { - connect(this, &QLineEdit::editingFinished, this, &SmartLineEdit::onLineChanged); - return; } diff --git a/ui/waterDialog.ui b/ui/waterDialog.ui index c7268c24..253af65b 100644 --- a/ui/waterDialog.ui +++ b/ui/waterDialog.ui @@ -167,7 +167,7 @@
    - + Ca @@ -178,7 +178,7 @@ - + Mg @@ -197,28 +197,28 @@ - + pH - + Na - + Cl - + SO<sub>4</sub> @@ -233,7 +233,7 @@ - + HCO<sub>3</sub> From abe426de74a39e9da5224d5d1721d3b6bbd5ddb0 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Fri, 5 May 2023 08:14:22 +0200 Subject: [PATCH 28/42] Start pulling out common base from Editors --- bt | 2 +- meson.build | 40 ++-- src/BtTreeView.cpp | 6 +- src/CMakeLists.txt | 20 +- src/FermentableDialog.cpp | 2 +- src/HopDialog.cpp | 49 ++--- src/HopEditor.cpp | 264 ------------------------ src/MainWindow.cpp | 31 +-- src/MiscDialog.cpp | 2 +- src/WaterDialog.cpp | 2 +- src/YeastDialog.cpp | 2 +- src/database/DatabaseSchemaHelper.cpp | 2 +- src/database/ObjectStoreTyped.cpp | 2 +- src/editors/EditorBase.h | 195 +++++++++++++++++ src/{ => editors}/EquipmentEditor.cpp | 4 +- src/{ => editors}/EquipmentEditor.h | 6 +- src/{ => editors}/FermentableEditor.cpp | 101 +++++---- src/{ => editors}/FermentableEditor.h | 12 +- src/editors/HopEditor.cpp | 176 ++++++++++++++++ src/{ => editors}/HopEditor.h | 33 ++- src/{ => editors}/MashEditor.cpp | 4 +- src/{ => editors}/MashEditor.h | 6 +- src/{ => editors}/MashStepEditor.cpp | 4 +- src/{ => editors}/MashStepEditor.h | 6 +- src/{ => editors}/MiscEditor.cpp | 4 +- src/{ => editors}/MiscEditor.h | 6 +- src/{ => editors}/NamedMashEditor.cpp | 4 +- src/{ => editors}/NamedMashEditor.h | 8 +- src/{ => editors}/StyleEditor.cpp | 4 +- src/{ => editors}/StyleEditor.h | 6 +- src/{ => editors}/WaterEditor.cpp | 4 +- src/{ => editors}/WaterEditor.h | 6 +- src/{ => editors}/YeastEditor.cpp | 4 +- src/{ => editors}/YeastEditor.h | 6 +- src/json/BeerJson.cpp | 2 +- src/json/JsonRecord.cpp | 16 +- src/model/Hop.cpp | 10 +- src/model/Hop.h | 17 +- src/model/Water.h | 2 +- src/tableModels/BtTableModel.h | 1 + src/widgets/SmartAmounts.h | 2 +- src/xml/BeerXml.cpp | 80 ++++--- translations/bt_ca.ts | 28 +++ translations/bt_cs.ts | 28 +++ translations/bt_de.ts | 28 +++ translations/bt_el.ts | 28 +++ translations/bt_en.ts | 28 +++ translations/bt_es.ts | 28 +++ translations/bt_et.ts | 28 +++ translations/bt_eu.ts | 28 +++ translations/bt_fr.ts | 28 +++ translations/bt_gl.ts | 28 +++ translations/bt_hu.ts | 28 +++ translations/bt_it.ts | 28 +++ translations/bt_lv.ts | 28 +++ translations/bt_nb.ts | 28 +++ translations/bt_nl.ts | 28 +++ translations/bt_pl.ts | 28 +++ translations/bt_pt.ts | 28 +++ translations/bt_ru.ts | 28 +++ translations/bt_sr.ts | 28 +++ translations/bt_sv.ts | 28 +++ translations/bt_tr.ts | 28 +++ translations/bt_zh.ts | 28 +++ ui/fermentableEditor.ui | 104 ++++++++++ 65 files changed, 1362 insertions(+), 511 deletions(-) delete mode 100644 src/HopEditor.cpp create mode 100644 src/editors/EditorBase.h rename src/{ => editors}/EquipmentEditor.cpp (99%) rename src/{ => editors}/EquipmentEditor.h (94%) rename src/{ => editors}/FermentableEditor.cpp (51%) rename src/{ => editors}/FermentableEditor.h (89%) create mode 100644 src/editors/HopEditor.cpp rename src/{ => editors}/HopEditor.h (69%) rename src/{ => editors}/MashEditor.cpp (98%) rename src/{ => editors}/MashEditor.h (92%) rename src/{ => editors}/MashStepEditor.cpp (98%) rename src/{ => editors}/MashStepEditor.h (93%) rename src/{ => editors}/MiscEditor.cpp (98%) rename src/{ => editors}/MiscEditor.h (93%) rename src/{ => editors}/NamedMashEditor.cpp (98%) rename src/{ => editors}/NamedMashEditor.h (94%) rename src/{ => editors}/StyleEditor.cpp (99%) rename src/{ => editors}/StyleEditor.h (93%) rename src/{ => editors}/WaterEditor.cpp (99%) rename src/{ => editors}/WaterEditor.h (93%) rename src/{ => editors}/YeastEditor.cpp (98%) rename src/{ => editors}/YeastEditor.h (91%) diff --git a/bt b/bt index d835153f..61d1196f 100755 --- a/bt +++ b/bt @@ -1158,7 +1158,7 @@ def doPackage(): # # Debian and RPM both want the debugging information stripped from the executable. # - # .:TBD:. One day perhaps we could be friendlyi and still ship the debugging info, just in a separate .dbg + # .:TBD:. One day perhaps we could be friendly and still ship the debugging info, just in a separate .dbg # file. The procedure to do this is described in the 'only-keep-debug' section of `man objcopy`. However, we # need to work out where to put the .dbg file so that it remains usable but lintian does not complain about it. # diff --git a/meson.build b/meson.build index ed27f7ee..750c2239 100644 --- a/meson.build +++ b/meson.build @@ -606,16 +606,23 @@ commonSourceFiles = files([ 'src/database/DbTransaction.cpp', 'src/database/ObjectStore.cpp', 'src/database/ObjectStoreTyped.cpp', + 'src/editors/EquipmentEditor.cpp', + 'src/editors/FermentableEditor.cpp', + 'src/editors/HopEditor.cpp', + 'src/editors/MashEditor.cpp', + 'src/editors/MashStepEditor.cpp', + 'src/editors/MiscEditor.cpp', + 'src/editors/NamedMashEditor.cpp', + 'src/editors/StyleEditor.cpp', + 'src/editors/WaterEditor.cpp', + 'src/editors/YeastEditor.cpp', 'src/EquipmentButton.cpp', - 'src/EquipmentEditor.cpp', 'src/EquipmentListModel.cpp', 'src/FermentableDialog.cpp', - 'src/FermentableEditor.cpp', 'src/FermentableSortFilterProxyModel.cpp', 'src/HeatCalculations.cpp', 'src/HelpDialog.cpp', 'src/HopDialog.cpp', - 'src/HopEditor.cpp', 'src/HopSortFilterProxyModel.cpp', 'src/Html.cpp', 'src/HydrometerTool.cpp', @@ -637,9 +644,7 @@ commonSourceFiles = files([ 'src/MashButton.cpp', 'src/MashComboBox.cpp', 'src/MashDesigner.cpp', - 'src/MashEditor.cpp', 'src/MashListModel.cpp', - 'src/MashStepEditor.cpp', 'src/MashStepTableWidget.cpp', 'src/MashWizard.cpp', 'src/matrix.cpp', @@ -654,7 +659,6 @@ commonSourceFiles = files([ 'src/measurement/Unit.cpp', 'src/measurement/UnitSystem.cpp', 'src/MiscDialog.cpp', - 'src/MiscEditor.cpp', 'src/MiscSortFilterProxyModel.cpp', 'src/model/BrewNote.cpp', 'src/model/Equipment.cpp', @@ -674,7 +678,6 @@ commonSourceFiles = files([ 'src/model/Water.cpp', 'src/model/Yeast.cpp', 'src/NamedEntitySortProxyModel.cpp', - 'src/NamedMashEditor.cpp', 'src/OgAdjuster.cpp', 'src/OptionDialog.cpp', 'src/PersistentSettings.cpp', @@ -691,7 +694,6 @@ commonSourceFiles = files([ 'src/SimpleUndoableUpdate.cpp', 'src/StrikeWaterDialog.cpp', 'src/StyleButton.cpp', - 'src/StyleEditor.cpp', 'src/StyleListModel.cpp', 'src/StyleRangeWidget.cpp', 'src/StyleSortFilterProxyModel.cpp', @@ -717,7 +719,6 @@ commonSourceFiles = files([ 'src/utils/TypeLookup.cpp', 'src/WaterButton.cpp', 'src/WaterDialog.cpp', - 'src/WaterEditor.cpp', 'src/WaterListModel.cpp', 'src/WaterSortFilterProxyModel.cpp', 'src/WaterTableWidget.cpp', @@ -739,7 +740,6 @@ commonSourceFiles = files([ 'src/xml/XmlRecipeRecord.cpp', 'src/xml/XmlRecord.cpp', 'src/YeastDialog.cpp', - 'src/YeastEditor.cpp', 'src/YeastSortFilterProxyModel.cpp', ]) @@ -778,15 +778,22 @@ mocHeaders = files([ 'src/ConverterTool.h', 'src/CustomComboBox.h', 'src/database/ObjectStore.h', + 'src/editors/EquipmentEditor.h', + 'src/editors/FermentableEditor.h', + 'src/editors/HopEditor.h', + 'src/editors/MashEditor.h', + 'src/editors/MashStepEditor.h', + 'src/editors/MiscEditor.h', + 'src/editors/NamedMashEditor.h', + 'src/editors/StyleEditor.h', + 'src/editors/WaterEditor.h', + 'src/editors/YeastEditor.h', 'src/EquipmentButton.h', - 'src/EquipmentEditor.h', 'src/EquipmentListModel.h', 'src/FermentableDialog.h', - 'src/FermentableEditor.h', 'src/FermentableSortFilterProxyModel.h', 'src/HelpDialog.h', 'src/HopDialog.h', - 'src/HopEditor.h', 'src/HopSortFilterProxyModel.h', 'src/HydrometerTool.h', 'src/IbuGuSlider.h', @@ -795,13 +802,10 @@ mocHeaders = files([ 'src/MashButton.h', 'src/MashComboBox.h', 'src/MashDesigner.h', - 'src/MashEditor.h', 'src/MashListModel.h', - 'src/MashStepEditor.h', 'src/MashStepTableWidget.h', 'src/MashWizard.h', 'src/MiscDialog.h', - 'src/MiscEditor.h', 'src/MiscSortFilterProxyModel.h', 'src/model/BrewNote.h', 'src/model/Equipment.h', @@ -820,7 +824,6 @@ mocHeaders = files([ 'src/model/Water.h', 'src/model/Yeast.h', 'src/NamedEntitySortProxyModel.h', - 'src/NamedMashEditor.h', 'src/OgAdjuster.h', 'src/OptionDialog.h', 'src/PitchDialog.h', @@ -834,7 +837,6 @@ mocHeaders = files([ 'src/SimpleUndoableUpdate.h', 'src/StrikeWaterDialog.h', 'src/StyleButton.h', - 'src/StyleEditor.h', 'src/StyleListModel.h', 'src/StyleRangeWidget.h', 'src/StyleSortFilterProxyModel.h', @@ -851,7 +853,6 @@ mocHeaders = files([ 'src/TimerWidget.h', 'src/WaterButton.h', 'src/WaterDialog.h', - 'src/WaterEditor.h', 'src/WaterListModel.h', 'src/WaterSortFilterProxyModel.h', 'src/WaterTableWidget.h', @@ -862,7 +863,6 @@ mocHeaders = files([ 'src/widgets/SmartLineEdit.h', 'src/widgets/ToggleSwitch.h', 'src/YeastDialog.h', - 'src/YeastEditor.h', 'src/YeastSortFilterProxyModel.h', ]) diff --git a/src/BtTreeView.cpp b/src/BtTreeView.cpp index 8a51b438..7b070a26 100644 --- a/src/BtTreeView.cpp +++ b/src/BtTreeView.cpp @@ -32,7 +32,7 @@ #include "BtFolder.h" #include "BtTreeFilterProxyModel.h" #include "BtTreeModel.h" -#include "EquipmentEditor.h" +#include "editors/EquipmentEditor.h" #include "FermentableDialog.h" #include "HopDialog.h" #include "MiscDialog.h" @@ -45,8 +45,8 @@ #include "model/Style.h" #include "model/Water.h" #include "model/Yeast.h" -#include "StyleEditor.h" -#include "WaterEditor.h" +#include "editors/StyleEditor.h" +#include "editors/WaterEditor.h" #include "YeastDialog.h" BtTreeView::BtTreeView(QWidget * parent, BtTreeModel::TypeMasks type) : diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 03982716..1477f6cc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -67,16 +67,23 @@ set(filesToCompile_cpp ${repoDir}/src/database/DbTransaction.cpp ${repoDir}/src/database/ObjectStore.cpp ${repoDir}/src/database/ObjectStoreTyped.cpp + ${repoDir}/src/editors/EquipmentEditor.cpp + ${repoDir}/src/editors/FermentableEditor.cpp + ${repoDir}/src/editors/HopEditor.cpp + ${repoDir}/src/editors/MashEditor.cpp + ${repoDir}/src/editors/MashStepEditor.cpp + ${repoDir}/src/editors/MiscEditor.cpp + ${repoDir}/src/editors/NamedMashEditor.cpp + ${repoDir}/src/editors/StyleEditor.cpp + ${repoDir}/src/editors/WaterEditor.cpp + ${repoDir}/src/editors/YeastEditor.cpp ${repoDir}/src/EquipmentButton.cpp - ${repoDir}/src/EquipmentEditor.cpp ${repoDir}/src/EquipmentListModel.cpp ${repoDir}/src/FermentableDialog.cpp - ${repoDir}/src/FermentableEditor.cpp ${repoDir}/src/FermentableSortFilterProxyModel.cpp ${repoDir}/src/HeatCalculations.cpp ${repoDir}/src/HelpDialog.cpp ${repoDir}/src/HopDialog.cpp - ${repoDir}/src/HopEditor.cpp ${repoDir}/src/HopSortFilterProxyModel.cpp ${repoDir}/src/Html.cpp ${repoDir}/src/HydrometerTool.cpp @@ -98,9 +105,7 @@ set(filesToCompile_cpp ${repoDir}/src/MashButton.cpp ${repoDir}/src/MashComboBox.cpp ${repoDir}/src/MashDesigner.cpp - ${repoDir}/src/MashEditor.cpp ${repoDir}/src/MashListModel.cpp - ${repoDir}/src/MashStepEditor.cpp ${repoDir}/src/MashStepTableWidget.cpp ${repoDir}/src/MashWizard.cpp ${repoDir}/src/matrix.cpp @@ -115,7 +120,6 @@ set(filesToCompile_cpp ${repoDir}/src/measurement/Unit.cpp ${repoDir}/src/measurement/UnitSystem.cpp ${repoDir}/src/MiscDialog.cpp - ${repoDir}/src/MiscEditor.cpp ${repoDir}/src/MiscSortFilterProxyModel.cpp ${repoDir}/src/model/BrewNote.cpp ${repoDir}/src/model/Equipment.cpp @@ -135,7 +139,6 @@ set(filesToCompile_cpp ${repoDir}/src/model/Water.cpp ${repoDir}/src/model/Yeast.cpp ${repoDir}/src/NamedEntitySortProxyModel.cpp - ${repoDir}/src/NamedMashEditor.cpp ${repoDir}/src/OgAdjuster.cpp ${repoDir}/src/OptionDialog.cpp ${repoDir}/src/PersistentSettings.cpp @@ -152,7 +155,6 @@ set(filesToCompile_cpp ${repoDir}/src/SimpleUndoableUpdate.cpp ${repoDir}/src/StrikeWaterDialog.cpp ${repoDir}/src/StyleButton.cpp - ${repoDir}/src/StyleEditor.cpp ${repoDir}/src/StyleListModel.cpp ${repoDir}/src/StyleRangeWidget.cpp ${repoDir}/src/StyleSortFilterProxyModel.cpp @@ -178,7 +180,6 @@ set(filesToCompile_cpp ${repoDir}/src/utils/TypeLookup.cpp ${repoDir}/src/WaterButton.cpp ${repoDir}/src/WaterDialog.cpp - ${repoDir}/src/WaterEditor.cpp ${repoDir}/src/WaterListModel.cpp ${repoDir}/src/WaterSortFilterProxyModel.cpp ${repoDir}/src/WaterTableWidget.cpp @@ -200,6 +201,5 @@ set(filesToCompile_cpp ${repoDir}/src/xml/XmlRecipeRecord.cpp ${repoDir}/src/xml/XmlRecord.cpp ${repoDir}/src/YeastDialog.cpp - ${repoDir}/src/YeastEditor.cpp ${repoDir}/src/YeastSortFilterProxyModel.cpp ) diff --git a/src/FermentableDialog.cpp b/src/FermentableDialog.cpp index 8ba63d75..b44df829 100644 --- a/src/FermentableDialog.cpp +++ b/src/FermentableDialog.cpp @@ -27,7 +27,7 @@ //#include "database/Database.h" #include "database/ObjectStoreWrapper.h" -#include "FermentableEditor.h" +#include "editors/FermentableEditor.h" #include "FermentableSortFilterProxyModel.h" #include "MainWindow.h" #include "model/Fermentable.h" diff --git a/src/HopDialog.cpp b/src/HopDialog.cpp index f59b5c12..d79a2659 100644 --- a/src/HopDialog.cpp +++ b/src/HopDialog.cpp @@ -28,7 +28,7 @@ #include #include "database/ObjectStoreWrapper.h" -#include "HopEditor.h" +#include "editors/HopEditor.h" #include "HopSortFilterProxyModel.h" #include "MainWindow.h" #include "model/Hop.h" @@ -106,8 +106,7 @@ void HopDialog::doLayout() { QMetaObject::connectSlotsByName(this); } -void HopDialog::retranslateUi() -{ +void HopDialog::retranslateUi() { setWindowTitle(tr("Hop Database")); pushButton_addToRecipe->setText(tr("Add to Recipe")); pushButton_new->setText(tr("New")); @@ -119,25 +118,24 @@ void HopDialog::retranslateUi() pushButton_edit->setToolTip(tr("Edit selected ingredient")); pushButton_remove->setToolTip(tr("Remove selected ingredient")); #endif // QT_NO_TOOLTIP + return; } void HopDialog::removeHop() { - QModelIndex modelIndex, viewIndex; QModelIndexList selected = tableWidget->selectionModel()->selectedIndexes(); - int row, size, i; - - size = selected.size(); - if (size == 0) + auto size = selected.size(); + if (size == 0) { return; + } // Make sure only one row is selected. - row = selected[0].row(); - for (i = 1; i < size; ++i) - { - if (selected[i].row() != row) + auto row = selected[0].row(); + for (int i = 1; i < size; ++i) { + if (selected[i].row() != row) { return; + } } - modelIndex = hopTableProxy->mapToSource(selected[0]); + QModelIndex modelIndex = hopTableProxy->mapToSource(selected[0]); auto hop = hopTableModel->getRow(modelIndex.row()); if (hop) { ObjectStoreWrapper::softDelete(*hop); @@ -200,31 +198,18 @@ void HopDialog::editSelected() translated = hopTableProxy->mapToSource(selected.value(0)); auto hop = hopTableModel->getRow(translated.row()); - hopEditor->setHop(hop.get()); + hopEditor->setEditItem(hop); hopEditor->show(); return; } void HopDialog::newHop(QString folder) { - QString name = QInputDialog::getText(this, tr("Hop name"), - tr("Hop name:")); - if( name.isEmpty() ) { - return; - } - - // .:TODO:. Change to shared_ptr as potential memory leak - Hop* hop = new Hop(name); - if ( ! folder.isEmpty() ) { - hop->setFolder(folder); - } - - hopEditor->setHop(hop); - hopEditor->show(); + hopEditor->newEditItem(folder); return; } -void HopDialog::filterHops(QString searchExpression) -{ - hopTableProxy->setFilterCaseSensitivity(Qt::CaseInsensitive); - hopTableProxy->setFilterFixedString(searchExpression); +void HopDialog::filterHops(QString searchExpression) { + hopTableProxy->setFilterCaseSensitivity(Qt::CaseInsensitive); + hopTableProxy->setFilterFixedString(searchExpression); + return; } diff --git a/src/HopEditor.cpp b/src/HopEditor.cpp deleted file mode 100644 index ce06734f..00000000 --- a/src/HopEditor.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/*====================================================================================================================== - * HopEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Kregg Kemper - * • Matt Young - * • Mik Firestone - * • Philip Greggory Lee - * • Samuel Östling - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "HopEditor.h" - -#include -#include -#include - -#include "BtHorizontalTabs.h" -#include "config.h" -#include "database/ObjectStoreWrapper.h" -#include "measurement/Unit.h" -#include "model/Hop.h" - -HopEditor::HopEditor(QWidget * parent) : - QDialog(parent), - obsHop(nullptr) { - 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 , 0); - SMART_FIELD_INIT(HopEditor, label_inventory , lineEdit_inventory , Hop, PropertyNames::Hop::amount_kg ); - SMART_FIELD_INIT(HopEditor, label_time , lineEdit_time , Hop, PropertyNames::Hop::time_min , 0); - SMART_FIELD_INIT(HopEditor, label_beta , lineEdit_beta , Hop, PropertyNames::Hop::beta_pct , 0); - 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 , 0); - SMART_FIELD_INIT(HopEditor, label_caryophyllene , lineEdit_caryophyllene , Hop, PropertyNames::Hop::caryophyllene_pct , 0); - SMART_FIELD_INIT(HopEditor, label_cohumulone , lineEdit_cohumulone , Hop, PropertyNames::Hop::cohumulone_pct , 0); - SMART_FIELD_INIT(HopEditor, label_myrcene , lineEdit_myrcene , Hop, PropertyNames::Hop::myrcene_pct , 0); - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - SMART_FIELD_INIT(HopEditor, label_producer , lineEdit_producer , Hop, PropertyNames::Hop::producer ); - SMART_FIELD_INIT(HopEditor, label_product_id , lineEdit_product_id , Hop, PropertyNames::Hop::product_id ); - SMART_FIELD_INIT(HopEditor, label_year , lineEdit_year , Hop, PropertyNames::Hop::year ); - SMART_FIELD_INIT(HopEditor, label_total_oil_ml_per_100g, lineEdit_total_oil_ml_per_100g, Hop, PropertyNames::Hop::total_oil_ml_per_100g ); - SMART_FIELD_INIT(HopEditor, label_farnesene , lineEdit_farnesene , Hop, PropertyNames::Hop::farnesene_pct , 0); - SMART_FIELD_INIT(HopEditor, label_geraniol , lineEdit_geraniol , Hop, PropertyNames::Hop::geraniol_pct , 0); - SMART_FIELD_INIT(HopEditor, label_b_pinene , lineEdit_b_pinene , Hop, PropertyNames::Hop::b_pinene_pct , 0); - SMART_FIELD_INIT(HopEditor, label_linalool , lineEdit_linalool , Hop, PropertyNames::Hop::linalool_pct , 0); - SMART_FIELD_INIT(HopEditor, label_limonene , lineEdit_limonene , Hop, PropertyNames::Hop::limonene_pct , 0); - SMART_FIELD_INIT(HopEditor, label_nerol , lineEdit_nerol , Hop, PropertyNames::Hop::nerol_pct , 0); - SMART_FIELD_INIT(HopEditor, label_pinene , lineEdit_pinene , Hop, PropertyNames::Hop::pinene_pct , 0); - SMART_FIELD_INIT(HopEditor, label_polyphenols , lineEdit_polyphenols , Hop, PropertyNames::Hop::polyphenols_pct , 0); - SMART_FIELD_INIT(HopEditor, label_xanthohumol , lineEdit_xanthohumol , Hop, PropertyNames::Hop::xanthohumol_pct , 0); - - // - // According to https://bugreports.qt.io/browse/QTBUG-50823 it is never going to be possible to specify the data (as - // opposed to display text) for a combo box via the .ui file. So we have to do it in code instead. - // We could use the raw enum values as the data, but it would be a bit painful to debug if we ever had to, so for - // small extra effort we use the same serialisation strings that we use for BeerJSON and the DB. - // - for (auto ii : Hop::allTypes) { - this->comboBox_hopType->addItem(Hop::typeDisplayNames[ii], Hop::typeStringMapping.enumToString(ii)); - } - for (auto ii : Hop::allForms) { - this->comboBox_hopForm->addItem(Hop::formDisplayNames[ii], Hop::formStringMapping.enumToString(ii)); - } - for (auto ii : Hop::allUses) { - this->comboBox_hopUse->addItem (Hop::useDisplayNames[ii], Hop::useStringMapping.enumToString(ii)); - } - - connect(this->pushButton_new, &QAbstractButton::clicked, this, &HopEditor::clickedNewHop); - connect(this->pushButton_save, &QAbstractButton::clicked, this, &HopEditor::save); - connect(this->pushButton_cancel, &QAbstractButton::clicked, this, &HopEditor::clearAndClose); - - return; -} - -HopEditor::~HopEditor() = default; - -void HopEditor::setHop(Hop * h) { - if (obsHop) { - disconnect(obsHop, nullptr, this, nullptr); - } - - obsHop = h; - if (obsHop) { - connect(obsHop, &NamedEntity::changed, this, &HopEditor::changed); - showChanges(); - } - return; -} - -void HopEditor::save() { - if (!this->obsHop) { - setVisible(false); - return; - } - - this->obsHop->setName (this->lineEdit_name ->text ()); - this->obsHop->setAlpha_pct (this->lineEdit_alpha ->getNonOptValueAs()); - this->obsHop->setTime_min (this->lineEdit_time ->toCanonical().quantity ()); - this->obsHop->setBeta_pct (this->lineEdit_beta ->getNonOptValueAs()); - this->obsHop->setHsi_pct (this->lineEdit_HSI ->getNonOptValueAs()); - this->obsHop->setOrigin (this->lineEdit_origin ->text ()); - this->obsHop->setHumulene_pct (this->lineEdit_humulene ->getNonOptValueAs()); - this->obsHop->setCaryophyllene_pct(this->lineEdit_caryophyllene->getNonOptValueAs()); - this->obsHop->setCohumulone_pct (this->lineEdit_cohumulone ->getNonOptValueAs()); - this->obsHop->setMyrcene_pct (this->lineEdit_myrcene ->getNonOptValueAs()); - this->obsHop->setSubstitutes (this->textEdit_substitutes ->toPlainText ()); - this->obsHop->setNotes (this->textEdit_notes ->toPlainText ()); - - // - // It's a coding error if we don't recognise the values in our own combo boxes, so it's OK that we'd get a - // std::bad_optional_access exception in such a case - // - this->obsHop->setType(Hop::typeStringMapping.stringToEnum(comboBox_hopType->currentData().toString())); - this->obsHop->setForm(Hop::formStringMapping.stringToEnum(comboBox_hopForm->currentData().toString())); - this->obsHop->setUse (Hop::useStringMapping.stringToEnum (comboBox_hopUse->currentData().toString())); - - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - this->obsHop->setProducer (this->lineEdit_producer ->text ()); - this->obsHop->setProduct_id (this->lineEdit_product_id ->text ()); - this->obsHop->setYear (this->lineEdit_year ->getOptValueAs ()); - this->obsHop->setTotal_oil_ml_per_100g(this->lineEdit_total_oil_ml_per_100g->getOptValueAs()); - this->obsHop->setFarnesene_pct (this->lineEdit_farnesene ->getOptValueAs()); - this->obsHop->setGeraniol_pct (this->lineEdit_geraniol ->getOptValueAs()); - this->obsHop->setB_pinene_pct (this->lineEdit_b_pinene ->getOptValueAs()); - this->obsHop->setLinalool_pct (this->lineEdit_linalool ->getOptValueAs()); - this->obsHop->setLimonene_pct (this->lineEdit_limonene ->getOptValueAs()); - this->obsHop->setNerol_pct (this->lineEdit_nerol ->getOptValueAs()); - this->obsHop->setPinene_pct (this->lineEdit_pinene ->getOptValueAs()); - this->obsHop->setPolyphenols_pct (this->lineEdit_polyphenols ->getOptValueAs()); - this->obsHop->setXanthohumol_pct (this->lineEdit_xanthohumol ->getOptValueAs()); - - if (this->obsHop->key() < 0) { - ObjectStoreWrapper::insert(*this->obsHop); - } - - // do this late to make sure we've the row in the inventory table - this->obsHop->setInventoryAmount(lineEdit_inventory->toCanonical().quantity()); - setVisible(false); - return; -} - -void HopEditor::clearAndClose() { - setHop(nullptr); - setVisible(false); // Hide the window. -} - -void HopEditor::changed(QMetaProperty prop, QVariant /*val*/) { - if (sender() == obsHop) { - showChanges(&prop); - } -} - -void HopEditor::showChanges(QMetaProperty * prop) { - if (!this->obsHop) { - return; - } - - bool updateAll = false; - QString propName = ""; - - if (prop == nullptr) { - updateAll = true; - } else { - propName = prop->name(); - } - - if (updateAll || propName == PropertyNames::Hop::use) { - // As above, it's a coding error if there isn't a combo box entry corresponding to the Hop type - comboBox_hopUse->setCurrentIndex( - comboBox_hopUse->findData(Hop::useStringMapping.enumToString(obsHop->use())) - ); - if (!updateAll) { - return; - } - } - if (updateAll || propName == PropertyNames::Hop::type) { - // As above, it's a coding error if there isn't a combo box entry corresponding to the Hop type - comboBox_hopType->setCurrentIndex( - comboBox_hopType->findData(Hop::typeStringMapping.enumToString(obsHop->type())) - ); - if (!updateAll) { - return; - } - } - if (updateAll || propName == PropertyNames::Hop::form) { - // As above, it's a coding error if there isn't a combo box entry corresponding to the Hop form - comboBox_hopForm->setCurrentIndex( - comboBox_hopForm->findData(Hop::formStringMapping.enumToString(obsHop->form())) - ); - if (!updateAll) { - return; - } - } - if (updateAll || propName == PropertyNames::NamedEntity::name ) { this->lineEdit_name ->setText (obsHop->name ()); // Continues to next line - this->lineEdit_name ->setCursorPosition(0); // Continues to next line - this->tabWidget_editor ->setTabText(0, obsHop->name()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::origin ) { this->lineEdit_origin ->setText (obsHop->origin ()); // Continues to next line - this->lineEdit_origin ->setCursorPosition(0); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::alpha_pct ) { this->lineEdit_alpha ->setAmount (obsHop->alpha_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::time_min ) { this->lineEdit_time ->setAmount (obsHop->time_min ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::beta_pct ) { this->lineEdit_beta ->setAmount (obsHop->beta_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::hsi_pct ) { this->lineEdit_HSI ->setAmount (obsHop->hsi_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::humulene_pct ) { this->lineEdit_humulene ->setAmount (obsHop->humulene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::caryophyllene_pct ) { this->lineEdit_caryophyllene ->setAmount (obsHop->caryophyllene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::cohumulone_pct ) { this->lineEdit_cohumulone ->setAmount (obsHop->cohumulone_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::myrcene_pct ) { this->lineEdit_myrcene ->setAmount (obsHop->myrcene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::substitutes ) { this->textEdit_substitutes ->setPlainText(obsHop->substitutes ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::notes ) { this->textEdit_notes ->setPlainText(obsHop->notes ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::NamedEntityWithInventory::inventory) { this->lineEdit_inventory ->setAmount (obsHop->inventory ()); if (!updateAll) { return; } } - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - if (updateAll || propName == PropertyNames::Hop::producer ) { this->lineEdit_producer ->setText (obsHop->producer ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::product_id ) { this->lineEdit_product_id ->setText (obsHop->product_id ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::year ) { this->lineEdit_year ->setAmount (obsHop->year ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::total_oil_ml_per_100g ) { this->lineEdit_total_oil_ml_per_100g->setAmount (obsHop->total_oil_ml_per_100g()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::farnesene_pct ) { this->lineEdit_farnesene ->setAmount (obsHop->farnesene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::geraniol_pct ) { this->lineEdit_geraniol ->setAmount (obsHop->geraniol_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::b_pinene_pct ) { this->lineEdit_b_pinene ->setAmount (obsHop->b_pinene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::linalool_pct ) { this->lineEdit_linalool ->setAmount (obsHop->linalool_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::limonene_pct ) { this->lineEdit_limonene ->setAmount (obsHop->limonene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::nerol_pct ) { this->lineEdit_nerol ->setAmount (obsHop->nerol_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::pinene_pct ) { this->lineEdit_pinene ->setAmount (obsHop->pinene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::polyphenols_pct ) { this->lineEdit_polyphenols ->setAmount (obsHop->polyphenols_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::xanthohumol_pct ) { this->lineEdit_xanthohumol ->setAmount (obsHop->xanthohumol_pct ()); if (!updateAll) { return; } } - - return; -} - - -void HopEditor::newHop(QString folder) { - QString name = QInputDialog::getText(this, tr("Hop name"), tr("Hop name:")); - if (name.isEmpty()) { - return; - } - - // .:TODO:. Change this to shared_ptr as currently results in memory leak in clearAndClose() - Hop * h = new Hop(name); - - if (! folder.isEmpty()) { - h->setFolder(folder); - } - - setHop(h); - show(); - return; -} - -void HopEditor::clickedNewHop() { - newHop(QString()); - return; -} diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index bff9aeee..2f20e0c2 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -81,27 +81,27 @@ #include "ConverterTool.h" #include "database/Database.h" #include "database/ObjectStoreWrapper.h" -#include "EquipmentEditor.h" +#include "editors/EquipmentEditor.h" #include "EquipmentListModel.h" #include "FermentableDialog.h" -#include "FermentableEditor.h" +#include "editors/FermentableEditor.h" #include "FermentableSortFilterProxyModel.h" #include "HelpDialog.h" #include "HopDialog.h" -#include "HopEditor.h" +#include "editors/HopEditor.h" #include "HopSortFilterProxyModel.h" #include "Html.h" #include "HydrometerTool.h" #include "ImportExport.h" #include "InventoryFormatter.h" #include "MashDesigner.h" -#include "MashEditor.h" +#include "editors/MashEditor.h" #include "MashListModel.h" -#include "MashStepEditor.h" +#include "editors/MashStepEditor.h" #include "MashWizard.h" #include "measurement/Unit.h" #include "MiscDialog.h" -#include "MiscEditor.h" +#include "editors/MiscEditor.h" #include "MiscSortFilterProxyModel.h" #include "measurement/Measurement.h" #include "model/BrewNote.h" @@ -111,7 +111,7 @@ #include "model/Recipe.h" #include "model/Style.h" #include "model/Yeast.h" -#include "NamedMashEditor.h" +#include "editors/NamedMashEditor.h" #include "OgAdjuster.h" #include "OptionDialog.h" #include "PersistentSettings.h" @@ -124,7 +124,7 @@ #include "RelationalUndoableUpdate.h" #include "ScaleRecipeTool.h" #include "StrikeWaterDialog.h" -#include "StyleEditor.h" +#include "editors/StyleEditor.h" #include "StyleListModel.h" #include "StyleSortFilterProxyModel.h" #include "tableModels/FermentableTableModel.h" @@ -138,10 +138,10 @@ #include "utils/BtStringConst.h" #include "utils/OptionalHelpers.h" #include "WaterDialog.h" -#include "WaterEditor.h" +#include "editors/WaterEditor.h" #include "WaterListModel.h" #include "YeastDialog.h" -#include "YeastEditor.h" +#include "editors/YeastEditor.h" #include "YeastSortFilterProxyModel.h" namespace { @@ -1022,7 +1022,7 @@ void MainWindow::treeActivated(const QModelIndex &index) { { Hop* h = active->getItem(index); if (h) { - hopEditor->setHop(h); + hopEditor->setEditItem(ObjectStoreWrapper::getSharedFromRaw(h)); hopEditor->show(); } } @@ -2034,14 +2034,15 @@ void MainWindow::editSelectedMisc() { miscEditor->show(); } -void MainWindow::editSelectedHop() -{ +void MainWindow::editSelectedHop() { Hop* h = selectedHop(); - if( h == nullptr ) + if (h == nullptr) { return; + } - hopEditor->setHop(h); + hopEditor->setEditItem(ObjectStoreWrapper::getSharedFromRaw(h)); hopEditor->show(); + return; } void MainWindow::editSelectedYeast() diff --git a/src/MiscDialog.cpp b/src/MiscDialog.cpp index 02fe4b51..263e8850 100644 --- a/src/MiscDialog.cpp +++ b/src/MiscDialog.cpp @@ -30,7 +30,7 @@ #include "model/Recipe.h" #include "MainWindow.h" #include "model/Misc.h" -#include "MiscEditor.h" +#include "editors/MiscEditor.h" #include "MiscSortFilterProxyModel.h" #include "tableModels/MiscTableModel.h" diff --git a/src/WaterDialog.cpp b/src/WaterDialog.cpp index 2f6a248b..7e0bc56a 100644 --- a/src/WaterDialog.cpp +++ b/src/WaterDialog.cpp @@ -36,7 +36,7 @@ #include "tableModels/SaltTableModel.h" #include "tableModels/WaterTableModel.h" #include "WaterButton.h" -#include "WaterEditor.h" +#include "editors/WaterEditor.h" #include "WaterListModel.h" #include "WaterSortFilterProxyModel.h" #include "widgets/SmartDigitWidget.h" diff --git a/src/YeastDialog.cpp b/src/YeastDialog.cpp index c91cf3ca..6f6dea7b 100644 --- a/src/YeastDialog.cpp +++ b/src/YeastDialog.cpp @@ -30,7 +30,7 @@ #include "model/Recipe.h" #include "model/Yeast.h" #include "tableModels/YeastTableModel.h" -#include "YeastEditor.h" +#include "editors/YeastEditor.h" #include "YeastSortFilterProxyModel.h" YeastDialog::YeastDialog(MainWindow* parent) diff --git a/src/database/DatabaseSchemaHelper.cpp b/src/database/DatabaseSchemaHelper.cpp index cac4ed46..a06e50ae 100644 --- a/src/database/DatabaseSchemaHelper.cpp +++ b/src/database/DatabaseSchemaHelper.cpp @@ -547,7 +547,7 @@ namespace { {QString( "UPDATE hop SET form = 'leaf' WHERE form = 'Leaf'" )}, {QString("ALTER TABLE hop ADD COLUMN producer %1").arg(db.getDbNativeTypeName())}, {QString("ALTER TABLE hop ADD COLUMN product_id %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE hop ADD COLUMN year %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE hop ADD COLUMN year %1").arg(db.getDbNativeTypeName())}, {QString("ALTER TABLE hop ADD COLUMN total_oil_ml_per_100g %1").arg(db.getDbNativeTypeName())}, {QString("ALTER TABLE hop ADD COLUMN farnesene_pct %1").arg(db.getDbNativeTypeName())}, {QString("ALTER TABLE hop ADD COLUMN geraniol_pct %1").arg(db.getDbNativeTypeName())}, diff --git a/src/database/ObjectStoreTyped.cpp b/src/database/ObjectStoreTyped.cpp index 90dd85d2..ec28fbdd 100644 --- a/src/database/ObjectStoreTyped.cpp +++ b/src/database/ObjectStoreTyped.cpp @@ -212,7 +212,7 @@ namespace { // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ {ObjectStore::FieldType::String, "producer", PropertyNames::Hop::producer }, {ObjectStore::FieldType::String, "product_id", PropertyNames::Hop::product_id }, - {ObjectStore::FieldType::Int, "year", PropertyNames::Hop::year }, + {ObjectStore::FieldType::String, "year", PropertyNames::Hop::year }, {ObjectStore::FieldType::Double, "total_oil_ml_per_100g", PropertyNames::Hop::total_oil_ml_per_100g }, {ObjectStore::FieldType::Double, "farnesene_pct", PropertyNames::Hop::farnesene_pct }, {ObjectStore::FieldType::Double, "geraniol_pct", PropertyNames::Hop::geraniol_pct }, diff --git a/src/editors/EditorBase.h b/src/editors/EditorBase.h new file mode 100644 index 00000000..16e74e30 --- /dev/null +++ b/src/editors/EditorBase.h @@ -0,0 +1,195 @@ +/*====================================================================================================================== + * editors/EditorBase.h is part of Brewken, and is copyright the following authors 2023: + * • Matt Young + * + * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see + * . + =====================================================================================================================*/ +#ifndef EDITORS_EDITORBASE_H +#define EDITORS_EDITORBASE_H +#pragma once + +#include + +#include +#include + +#include "database/ObjectStoreWrapper.h" +#include "model/NamedEntity.h" + +/** + * \class EditorBase + * + * \brief As in other places where we want to use class templating, we have to use multiple inheritance because we can't + * template a class that ultimately inherits from \c QObject. However, with the magic of the Curiously Recurring + * Template Pattern, we can get past some of the limitations and avoid too much copy-and-paste code duplication. + * Eg: + * + * QObject Ui::hopEditor + * \ | + * ... | + * \ | EditorBase + * QDialog | / + * \ | / + * \ | / + * HopEditor + * + * Besides inheriting from \c QDialog, the derived class (eg \c HopEditor in the example above) needs to + * implement the following trivial slots: + * + * - \c void \c save() public slot, which should call \c EditorBase::doSave + * - \c void \c clearAndClose() public slot, which should call \c EditorBase::doClearAndClose + * - \c void \c changed(\c QMetaProperty, \c QVariant) public slot, which should call \c EditorBase::doChanged + * - \c void \c clickedNew() public slot, which should call \c EditorBase::newEditItem + * + * The code for the definition of these slot functions (which is "the same" for all editors) can be inserted in + * the implementation file using the EDITOR_COMMON_SLOT_DEFINITIONS macro. Eg, in HopEditor, we need: + * + * EDITOR_COMMON_SLOT_DEFINITIONS(HopEditor) + * + * 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). + */ +template +class EditorBase { +public: + EditorBase() : + m_derived{static_cast(this)}, + m_editItem{nullptr} { + return; + } + virtual ~EditorBase() = default; + + /** + * \brief Edit the given Hop, Fermentable, etc. + */ + void setEditItem(std::shared_ptr editItem) { + if (this->m_editItem) { + this->m_derived->disconnect(this->m_editItem.get(), nullptr, this->m_derived, nullptr); + } + this->m_editItem = editItem; + if (this->m_editItem) { + this->m_derived->connect(this->m_editItem.get(), &NamedEntity::changed, this->m_derived, &Derived::changed); + this->m_derived->readFieldsFromEditItem(std::nullopt); + } + return; + } + + /** + * \brief Create a new Hop, Fermentable, etc. + */ + void newEditItem(QString folder = "") { + QString name = QInputDialog::getText(this->m_derived, + QString(QObject::tr("%1 name")).arg(NE::staticMetaObject.className()), + QString(QObject::tr("%1 name:")).arg(NE::staticMetaObject.className())); + if (name.isEmpty()) { + return; + } + + auto ne = std::make_shared(name); + if (!folder.isEmpty()) { + ne->setFolder(folder); + } + + this->setEditItem(ne); + this->m_derived->show(); + return; + } + + /** + * \brief Subclass should call this from its \c save slot + */ + void doSave() { + if (!this->m_editItem) { + this->m_derived->setVisible(false); + return; + } + this->m_derived->writeFieldsToEditItem(); + if (this->m_editItem->key() < 0) { + ObjectStoreWrapper::insert(this->m_editItem); + } + this->m_derived->writeLateFieldsToEditItem(); + + this->m_derived->setVisible(false); + return; + } + + /** + * \brief Subclass should call this from its \c clearAndClose slot + */ + void doClearAndClose() { + this->setEditItem(nullptr); + this->m_derived->setVisible(false); // Hide the window. + return; + } + + /** + * \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. + */ + void doChanged(QObject * sender, QMetaProperty prop, [[maybe_unused]] QVariant val) { + if (this->m_editItem && sender == this->m_editItem.get()) { + this->m_derived->readFieldsFromEditItem(prop.name()); + } + return; + } + +protected: + /** + * \brief This is the 'this' pointer downcast to the derived class, which allows us to call non-virtual member + * functions in the derived class from this templated base class. + */ + Derived * m_derived; + + /** + * \brief This is the \c NamedEntity subclass object we are creating or editing. We are also "observing" it in the + * sense that, if any other part of the code changes its data, we'll get a signal so we can update our + * display. Historically therefore this member variable was called \c obsHop, \c obsFermentable, etc in each + * of the editor classes. + */ + std::shared_ptr m_editItem; + +}; + +/** + * \brief Derived classes should include this in their implementation file + */ +#define EDITOR_COMMON_SLOT_DEFINITIONS(EditorName) \ + void EditorName::save() { this->doSave(); return; } \ + void EditorName::clearAndClose() { this->doClearAndClose(); return; } \ + void HopEditor::changed(QMetaProperty prop, QVariant val) { this->doChanged(this->sender(), prop, val); return; } \ + void HopEditor::clickedNew() { this->newEditItem(); return;} + + +namespace EditorHelpers { + /** + * \brief Set value of a combo box from an enum val + */ + template + void setComboBoxVal(QComboBox & comboBox, EnumStringMapping const & enumStringMapping, EE const enumVal) { + // It's a coding error if there isn't a combo box entry corresponding to the Hop type + comboBox.setCurrentIndex(comboBox.findData(enumStringMapping.enumToString(enumVal))); + return; + } +} + +#endif diff --git a/src/EquipmentEditor.cpp b/src/editors/EquipmentEditor.cpp similarity index 99% rename from src/EquipmentEditor.cpp rename to src/editors/EquipmentEditor.cpp index de3b633b..bf767035 100644 --- a/src/EquipmentEditor.cpp +++ b/src/editors/EquipmentEditor.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * EquipmentEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: + * editors/EquipmentEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • A.J. Drobnich * • Brian Rower * • David Grundberg @@ -22,7 +22,7 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#include "EquipmentEditor.h" +#include "editors/EquipmentEditor.h" #include #include diff --git a/src/EquipmentEditor.h b/src/editors/EquipmentEditor.h similarity index 94% rename from src/EquipmentEditor.h rename to src/editors/EquipmentEditor.h index 99235e03..a6dfce69 100644 --- a/src/EquipmentEditor.h +++ b/src/editors/EquipmentEditor.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * EquipmentEditor.h is part of Brewken, and is copyright the following authors 2009-2023: + * editors/EquipmentEditor.h is part of Brewken, and is copyright the following authors 2009-2023: * • David Grundberg * • Jeff Bailey * • Matt Young @@ -18,8 +18,8 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#ifndef EQUIPMENTEDITOR_H -#define EQUIPMENTEDITOR_H +#ifndef EDITORS_EQUIPMENTEDITOR_H +#define EDITORS_EQUIPMENTEDITOR_H #pragma once #include "ui_equipmentEditor.h" diff --git a/src/FermentableEditor.cpp b/src/editors/FermentableEditor.cpp similarity index 51% rename from src/FermentableEditor.cpp rename to src/editors/FermentableEditor.cpp index 84e0fd74..7fcaf07f 100644 --- a/src/FermentableEditor.cpp +++ b/src/editors/FermentableEditor.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * FermentableEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: + * editors/FermentableEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Daniel Pettersson * • Kregg Kemper @@ -19,7 +19,7 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#include "FermentableEditor.h" +#include "editors/FermentableEditor.h" #include #include @@ -36,7 +36,7 @@ FermentableEditor::FermentableEditor(QWidget* parent) : this->tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs); - // See comment in HopEditor.cpp about combo box setup in Qt + // See comment in editors/HopEditor.cpp about combo box setup in Qt for (auto ii : Fermentable::allTypes) { this->comboBox_fermentableType->addItem(Fermentable::typeDisplayNames[ii], Fermentable::typeStringMapping.enumToString(ii)); @@ -55,9 +55,10 @@ FermentableEditor::FermentableEditor(QWidget* parent) : SMART_FIELD_INIT(FermentableEditor, label_origin , lineEdit_origin , Fermentable, PropertyNames::Fermentable::origin ); SMART_FIELD_INIT(FermentableEditor, label_supplier , lineEdit_supplier , Fermentable, PropertyNames::Fermentable::supplier ); - connect(pushButton_new, &QAbstractButton::clicked, this, &FermentableEditor::clickedNewFermentable); - connect(pushButton_save, &QAbstractButton::clicked, this, &FermentableEditor::save); - connect(pushButton_cancel, &QAbstractButton::clicked, this, &FermentableEditor::clearAndClose); + connect(this->pushButton_new, &QAbstractButton::clicked, this, &FermentableEditor::clickedNewFermentable); + connect(this->pushButton_save, &QAbstractButton::clicked, this, &FermentableEditor::save ); + connect(this->pushButton_cancel, &QAbstractButton::clicked, this, &FermentableEditor::clearAndClose ); + connect(this->checkBox_isWeight, &QCheckBox::toggled, this, &FermentableEditor::setIsWeight ); // ⮜⮜⮜ Added for BeerJSON support ⮞⮞⮞ return; } @@ -87,20 +88,22 @@ void FermentableEditor::save() { Fermentable::typeStringMapping.stringToEnum(comboBox_fermentableType->currentData().toString()) ); - obsFerm->setYield_pct (lineEdit_yield ->getNonOptValueAs()); - obsFerm->setColor_srm (lineEdit_color ->toCanonical().quantity()); - obsFerm->setAddAfterBoil (checkBox_addAfterBoil ->checkState() == Qt::Checked); - obsFerm->setOrigin (lineEdit_origin ->text()); - obsFerm->setSupplier (lineEdit_supplier ->text()); - obsFerm->setCoarseFineDiff_pct (lineEdit_coarseFineDiff->getNonOptValueAs()); - obsFerm->setMoisture_pct (lineEdit_moisture ->getNonOptValueAs()); - obsFerm->setDiastaticPower_lintner(lineEdit_diastaticPower->toCanonical().quantity()); - obsFerm->setProtein_pct (lineEdit_protein ->getNonOptValueAs()); - obsFerm->setMaxInBatch_pct (lineEdit_maxInBatch ->getNonOptValueAs()); - obsFerm->setRecommendMash (checkBox_recommendMash ->checkState() == Qt::Checked); - obsFerm->setIsMashed (checkBox_isMashed ->checkState() == Qt::Checked); - obsFerm->setIbuGalPerLb (lineEdit_ibuGalPerLb ->getNonOptValueAs()); // .:TBD:. No metric measure? - obsFerm->setNotes (textEdit_notes ->toPlainText()); + this->obsFerm->setYield_pct (this->lineEdit_yield ->getNonOptValueAs()); + this->obsFerm->setColor_srm (this->lineEdit_color ->toCanonical().quantity()); + this->obsFerm->setAddAfterBoil (this->checkBox_addAfterBoil ->checkState() == Qt::Checked); + this->obsFerm->setOrigin (this->lineEdit_origin ->text()); + this->obsFerm->setSupplier (this->lineEdit_supplier ->text()); + this->obsFerm->setCoarseFineDiff_pct (this->lineEdit_coarseFineDiff->getNonOptValueAs()); + this->obsFerm->setMoisture_pct (this->lineEdit_moisture ->getNonOptValueAs()); + this->obsFerm->setDiastaticPower_lintner(this->lineEdit_diastaticPower->toCanonical().quantity()); + this->obsFerm->setProtein_pct (this->lineEdit_protein ->getNonOptValueAs()); + this->obsFerm->setMaxInBatch_pct (this->lineEdit_maxInBatch ->getNonOptValueAs()); + this->obsFerm->setRecommendMash (this->checkBox_recommendMash ->checkState() == Qt::Checked); + this->obsFerm->setIsMashed (this->checkBox_isMashed ->checkState() == Qt::Checked); + this->obsFerm->setIbuGalPerLb (this->lineEdit_ibuGalPerLb ->getNonOptValueAs()); // .:TBD:. No metric measure? + this->obsFerm->setNotes (this->textEdit_notes ->toPlainText()); + // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ + this->obsFerm->setAmountIsWeight (this->checkBox_isWeight->checkState() == Qt::Checked); if (this->obsFerm->key() < 0) { ObjectStoreWrapper::insert(*this->obsFerm); @@ -117,6 +120,7 @@ void FermentableEditor::save() { void FermentableEditor::clearAndClose() { setFermentable(nullptr); setVisible(false); // Hide the window. + return; } void FermentableEditor::showChanges(QMetaProperty* metaProp) { @@ -133,7 +137,7 @@ void FermentableEditor::showChanges(QMetaProperty* metaProp) { } if (propName == PropertyNames::Fermentable::type || updateAll) { - // As above, it's a coding error if there isn't a combo box entry corresponding to the Hop type + // As above, it's a coding error if there isn't a combo box entry corresponding to the Fermentable type comboBox_fermentableType->setCurrentIndex( comboBox_fermentableType->findData(Fermentable::typeStringMapping.enumToString(obsFerm->type())) ); @@ -141,23 +145,27 @@ void FermentableEditor::showChanges(QMetaProperty* metaProp) { return; } } - if (updateAll || propName == PropertyNames::NamedEntity::name ) { lineEdit_name ->setText (obsFerm->name() ); // Continues to next line - lineEdit_name->setCursorPosition(0); tabWidget_editor->setTabText(0, obsFerm->name()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::NamedEntityWithInventory::inventory) { lineEdit_inventory ->setAmount (obsFerm->inventory() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::yield_pct ) { lineEdit_yield ->setAmount (obsFerm->yield_pct() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::color_srm ) { lineEdit_color ->setAmount (obsFerm->color_srm() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::addAfterBoil ) { checkBox_addAfterBoil ->setCheckState(obsFerm->addAfterBoil() ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::origin ) { lineEdit_origin ->setText (obsFerm->origin() ); lineEdit_origin ->setCursorPosition(0); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::supplier ) { lineEdit_supplier ->setText (obsFerm->supplier() ); lineEdit_supplier->setCursorPosition(0); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::coarseFineDiff_pct ) { lineEdit_coarseFineDiff->setAmount (obsFerm->coarseFineDiff_pct() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::moisture_pct ) { lineEdit_moisture ->setAmount (obsFerm->moisture_pct() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::diastaticPower_lintner) { lineEdit_diastaticPower->setAmount (obsFerm->diastaticPower_lintner()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::protein_pct ) { lineEdit_protein ->setAmount (obsFerm->protein_pct() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::maxInBatch_pct ) { lineEdit_maxInBatch ->setAmount (obsFerm->maxInBatch_pct() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::recommendMash ) { checkBox_recommendMash ->setCheckState(obsFerm->recommendMash() ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::isMashed ) { checkBox_isMashed ->setCheckState(obsFerm->isMashed() ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::ibuGalPerLb ) { lineEdit_ibuGalPerLb ->setAmount (obsFerm->ibuGalPerLb() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::notes ) { textEdit_notes ->setPlainText (obsFerm->notes() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::NamedEntity::name ) { this->lineEdit_name ->setText (obsFerm->name() ); // Continues to next line + this->lineEdit_name->setCursorPosition(0); tabWidget_editor->setTabText(0, obsFerm->name()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::NamedEntityWithInventory::inventory) { this->lineEdit_inventory ->setAmount (obsFerm->inventory() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::yield_pct ) { this->lineEdit_yield ->setAmount (obsFerm->yield_pct() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::color_srm ) { this->lineEdit_color ->setAmount (obsFerm->color_srm() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::addAfterBoil ) { this->checkBox_addAfterBoil ->setCheckState(obsFerm->addAfterBoil() ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::origin ) { this->lineEdit_origin ->setText (obsFerm->origin() ); lineEdit_origin ->setCursorPosition(0); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::supplier ) { this->lineEdit_supplier ->setText (obsFerm->supplier() ); lineEdit_supplier->setCursorPosition(0); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::coarseFineDiff_pct ) { this->lineEdit_coarseFineDiff->setAmount (obsFerm->coarseFineDiff_pct() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::moisture_pct ) { this->lineEdit_moisture ->setAmount (obsFerm->moisture_pct() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::diastaticPower_lintner) { this->lineEdit_diastaticPower->setAmount (obsFerm->diastaticPower_lintner()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::protein_pct ) { this->lineEdit_protein ->setAmount (obsFerm->protein_pct() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::maxInBatch_pct ) { this->lineEdit_maxInBatch ->setAmount (obsFerm->maxInBatch_pct() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::recommendMash ) { this->checkBox_recommendMash ->setCheckState(obsFerm->recommendMash() ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::isMashed ) { this->checkBox_isMashed ->setCheckState(obsFerm->isMashed() ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::ibuGalPerLb ) { this->lineEdit_ibuGalPerLb ->setAmount (obsFerm->ibuGalPerLb() ); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Fermentable::notes ) { this->textEdit_notes ->setPlainText (obsFerm->notes() ); if (!updateAll) { return; } } + // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ + if (updateAll || propName == PropertyNames::Fermentable::amountIsWeight ) { this->checkBox_isWeight ->setCheckState (obsFerm->amountIsWeight() ? // Continues to next line + Qt::Checked : Qt::Unchecked ); if (!updateAll) { return; } } + return; } @@ -183,3 +191,20 @@ void FermentableEditor::clickedNewFermentable() { newFermentable(QString()); return; } + +void FermentableEditor::setIsWeight(bool const state) { + qDebug() << Q_FUNC_INFO << "state is" << state; + // But you have to admit, this is clever + this->lineEdit_inventory->selectPhysicalQuantity( + state ? Measurement::PhysicalQuantity::Mass : Measurement::PhysicalQuantity::Volume + ); + + // maybe? My head hurts now + this->lineEdit_inventory->onLineChanged(); + + // Strictly, if we change a Fermentable to be measured by mass instead of volume (or vice versa) we should also somehow tell + // any other bit of the UI that is showing that Fermentable (eg a RecipeEditor or MainWindow) to redisplay the relevant + // field. Currently we don't do this, on the assumption that it's rare you will change how a Fermentable is measured after + // you started using it in recipes. + return; +} diff --git a/src/FermentableEditor.h b/src/editors/FermentableEditor.h similarity index 89% rename from src/FermentableEditor.h rename to src/editors/FermentableEditor.h index 5aa1a8b8..e07776e4 100644 --- a/src/FermentableEditor.h +++ b/src/editors/FermentableEditor.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * FermentableEditor.h is part of Brewken, and is copyright the following authors 2009-2022: + * editors/FermentableEditor.h is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Jeff Bailey * • Matt Young @@ -17,14 +17,15 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#ifndef FERMENTABLEEDITOR_H -#define FERMENTABLEEDITOR_H +#ifndef EDITORS_FERMENTABLEEDITOR_H +#define EDITORS_FERMENTABLEEDITOR_H #pragma once +#include "ui_fermentableEditor.h" + #include #include -#include -#include "ui_fermentableEditor.h" +#include // Forward declarations. class Fermentable; @@ -47,6 +48,7 @@ public slots: void save(); void clearAndClose(); void clickedNewFermentable(); + void setIsWeight(bool state); private: Fermentable* obsFerm; diff --git a/src/editors/HopEditor.cpp b/src/editors/HopEditor.cpp new file mode 100644 index 00000000..363e8c96 --- /dev/null +++ b/src/editors/HopEditor.cpp @@ -0,0 +1,176 @@ +/*====================================================================================================================== + * editors/HopEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: + * • Brian Rower + * • Kregg Kemper + * • Matt Young + * • Mik Firestone + * • Philip Greggory Lee + * • Samuel Östling + * + * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later + * version. + * + * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see + * . + =====================================================================================================================*/ +#include "editors/HopEditor.h" + +#include +#include +#include + +#include "BtHorizontalTabs.h" +#include "config.h" +#include "database/ObjectStoreWrapper.h" +#include "measurement/Unit.h" +#include "model/Hop.h" + +HopEditor::HopEditor(QWidget * parent) : + QDialog(parent), + EditorBase() { + 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 , 0); + SMART_FIELD_INIT(HopEditor, label_inventory , lineEdit_inventory , Hop, PropertyNames::Hop::amount_kg ); + SMART_FIELD_INIT(HopEditor, label_time , lineEdit_time , Hop, PropertyNames::Hop::time_min , 0); + SMART_FIELD_INIT(HopEditor, label_beta , lineEdit_beta , Hop, PropertyNames::Hop::beta_pct , 0); + 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 , 0); + SMART_FIELD_INIT(HopEditor, label_caryophyllene , lineEdit_caryophyllene , Hop, PropertyNames::Hop::caryophyllene_pct , 0); + SMART_FIELD_INIT(HopEditor, label_cohumulone , lineEdit_cohumulone , Hop, PropertyNames::Hop::cohumulone_pct , 0); + SMART_FIELD_INIT(HopEditor, label_myrcene , lineEdit_myrcene , Hop, PropertyNames::Hop::myrcene_pct , 0); + // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ + SMART_FIELD_INIT(HopEditor, label_producer , lineEdit_producer , Hop, PropertyNames::Hop::producer ); + SMART_FIELD_INIT(HopEditor, label_product_id , lineEdit_product_id , Hop, PropertyNames::Hop::product_id ); + SMART_FIELD_INIT(HopEditor, label_year , lineEdit_year , Hop, PropertyNames::Hop::year ); + SMART_FIELD_INIT(HopEditor, label_total_oil_ml_per_100g, lineEdit_total_oil_ml_per_100g, Hop, PropertyNames::Hop::total_oil_ml_per_100g ); + SMART_FIELD_INIT(HopEditor, label_farnesene , lineEdit_farnesene , Hop, PropertyNames::Hop::farnesene_pct , 0); + SMART_FIELD_INIT(HopEditor, label_geraniol , lineEdit_geraniol , Hop, PropertyNames::Hop::geraniol_pct , 0); + SMART_FIELD_INIT(HopEditor, label_b_pinene , lineEdit_b_pinene , Hop, PropertyNames::Hop::b_pinene_pct , 0); + SMART_FIELD_INIT(HopEditor, label_linalool , lineEdit_linalool , Hop, PropertyNames::Hop::linalool_pct , 0); + SMART_FIELD_INIT(HopEditor, label_limonene , lineEdit_limonene , Hop, PropertyNames::Hop::limonene_pct , 0); + SMART_FIELD_INIT(HopEditor, label_nerol , lineEdit_nerol , Hop, PropertyNames::Hop::nerol_pct , 0); + SMART_FIELD_INIT(HopEditor, label_pinene , lineEdit_pinene , Hop, PropertyNames::Hop::pinene_pct , 0); + SMART_FIELD_INIT(HopEditor, label_polyphenols , lineEdit_polyphenols , Hop, PropertyNames::Hop::polyphenols_pct , 0); + SMART_FIELD_INIT(HopEditor, label_xanthohumol , lineEdit_xanthohumol , Hop, PropertyNames::Hop::xanthohumol_pct , 0); + + // + // According to https://bugreports.qt.io/browse/QTBUG-50823 it is never going to be possible to specify the data (as + // opposed to display text) for a combo box via the .ui file. So we have to do it in code instead. + // We could use the raw enum values as the data, but it would be a bit painful to debug if we ever had to, so for + // small extra effort we use the same serialisation strings that we use for BeerJSON and the DB. + // + for (auto ii : Hop::allTypes) { + this->comboBox_hopType->addItem(Hop::typeDisplayNames[ii], Hop::typeStringMapping.enumToString(ii)); + } + for (auto ii : Hop::allForms) { + this->comboBox_hopForm->addItem(Hop::formDisplayNames[ii], Hop::formStringMapping.enumToString(ii)); + } + for (auto ii : Hop::allUses) { + this->comboBox_hopUse->addItem (Hop::useDisplayNames[ii], Hop::useStringMapping.enumToString(ii)); + } + + connect(this->pushButton_new, &QAbstractButton::clicked, this, &HopEditor::clickedNew); + connect(this->pushButton_save, &QAbstractButton::clicked, this, &HopEditor::save); + connect(this->pushButton_cancel, &QAbstractButton::clicked, this, &HopEditor::clearAndClose); + + return; +} + +HopEditor::~HopEditor() = default; + +void HopEditor::writeFieldsToEditItem() { + this->m_editItem->setName (this->lineEdit_name ->text ()); + this->m_editItem->setAlpha_pct (this->lineEdit_alpha ->getNonOptValueAs()); + this->m_editItem->setTime_min (this->lineEdit_time ->toCanonical().quantity ()); + this->m_editItem->setBeta_pct (this->lineEdit_beta ->getNonOptValueAs()); + this->m_editItem->setHsi_pct (this->lineEdit_HSI ->getNonOptValueAs()); + this->m_editItem->setOrigin (this->lineEdit_origin ->text ()); + this->m_editItem->setHumulene_pct (this->lineEdit_humulene ->getNonOptValueAs()); + this->m_editItem->setCaryophyllene_pct(this->lineEdit_caryophyllene->getNonOptValueAs()); + this->m_editItem->setCohumulone_pct (this->lineEdit_cohumulone ->getNonOptValueAs()); + this->m_editItem->setMyrcene_pct (this->lineEdit_myrcene ->getNonOptValueAs()); + this->m_editItem->setSubstitutes (this->textEdit_substitutes ->toPlainText ()); + this->m_editItem->setNotes (this->textEdit_notes ->toPlainText ()); + + // + // It's a coding error if we don't recognise the values in our own combo boxes, so it's OK that we'd get a + // std::bad_optional_access exception in such a case + // + this->m_editItem->setType(Hop::typeStringMapping.stringToEnum(comboBox_hopType->currentData().toString())); + this->m_editItem->setForm(Hop::formStringMapping.stringToEnum(comboBox_hopForm->currentData().toString())); + this->m_editItem->setUse (Hop::useStringMapping.stringToEnum (comboBox_hopUse->currentData().toString())); + + // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ + this->m_editItem->setProducer (this->lineEdit_producer ->text ()); + this->m_editItem->setProduct_id (this->lineEdit_product_id ->text ()); + this->m_editItem->setYear (this->lineEdit_year ->text ()); + this->m_editItem->setTotal_oil_ml_per_100g(this->lineEdit_total_oil_ml_per_100g->getOptValueAs()); + this->m_editItem->setFarnesene_pct (this->lineEdit_farnesene ->getOptValueAs()); + this->m_editItem->setGeraniol_pct (this->lineEdit_geraniol ->getOptValueAs()); + this->m_editItem->setB_pinene_pct (this->lineEdit_b_pinene ->getOptValueAs()); + this->m_editItem->setLinalool_pct (this->lineEdit_linalool ->getOptValueAs()); + this->m_editItem->setLimonene_pct (this->lineEdit_limonene ->getOptValueAs()); + this->m_editItem->setNerol_pct (this->lineEdit_nerol ->getOptValueAs()); + this->m_editItem->setPinene_pct (this->lineEdit_pinene ->getOptValueAs()); + this->m_editItem->setPolyphenols_pct (this->lineEdit_polyphenols ->getOptValueAs()); + this->m_editItem->setXanthohumol_pct (this->lineEdit_xanthohumol ->getOptValueAs()); + return; +} + +void HopEditor::writeLateFieldsToEditItem() { + // do this late to make sure we've the row in the inventory table + this->m_editItem->setInventoryAmount(lineEdit_inventory->toCanonical().quantity()); + return; +} + +void HopEditor::readFieldsFromEditItem(std::optional propName) { + if (!propName || *propName == PropertyNames::Hop::use ) { EditorHelpers::setComboBoxVal(*this->comboBox_hopUse , Hop:: useStringMapping, m_editItem->use ()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::type) { EditorHelpers::setComboBoxVal(*this->comboBox_hopType, Hop::typeStringMapping, m_editItem->type()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::form) { EditorHelpers::setComboBoxVal(*this->comboBox_hopForm, Hop::formStringMapping, m_editItem->form()); if (propName) { return; } } + + if (!propName || *propName == PropertyNames::NamedEntity::name ) { this->lineEdit_name ->setText (m_editItem->name ()); // Continues to next line + this->lineEdit_name ->setCursorPosition(0); // Continues to next line + this->tabWidget_editor ->setTabText(0, m_editItem->name()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::origin ) { this->lineEdit_origin ->setText (m_editItem->origin ()); // Continues to next line + this->lineEdit_origin ->setCursorPosition(0); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::alpha_pct ) { this->lineEdit_alpha ->setAmount (m_editItem->alpha_pct ()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::time_min ) { this->lineEdit_time ->setAmount (m_editItem->time_min ()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::beta_pct ) { this->lineEdit_beta ->setAmount (m_editItem->beta_pct ()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::hsi_pct ) { this->lineEdit_HSI ->setAmount (m_editItem->hsi_pct ()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::humulene_pct ) { this->lineEdit_humulene ->setAmount (m_editItem->humulene_pct ()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::caryophyllene_pct ) { this->lineEdit_caryophyllene ->setAmount (m_editItem->caryophyllene_pct ()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::cohumulone_pct ) { this->lineEdit_cohumulone ->setAmount (m_editItem->cohumulone_pct ()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::myrcene_pct ) { this->lineEdit_myrcene ->setAmount (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::NamedEntityWithInventory::inventory) { this->lineEdit_inventory ->setAmount (m_editItem->inventory ()); 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::product_id ) { this->lineEdit_product_id ->setText (m_editItem->product_id ()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::year ) { this->lineEdit_year ->setText (m_editItem->year ()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::total_oil_ml_per_100g ) { this->lineEdit_total_oil_ml_per_100g->setAmount (m_editItem->total_oil_ml_per_100g()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::farnesene_pct ) { this->lineEdit_farnesene ->setAmount (m_editItem->farnesene_pct ()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::geraniol_pct ) { this->lineEdit_geraniol ->setAmount (m_editItem->geraniol_pct ()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::b_pinene_pct ) { this->lineEdit_b_pinene ->setAmount (m_editItem->b_pinene_pct ()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::linalool_pct ) { this->lineEdit_linalool ->setAmount (m_editItem->linalool_pct ()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::limonene_pct ) { this->lineEdit_limonene ->setAmount (m_editItem->limonene_pct ()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::nerol_pct ) { this->lineEdit_nerol ->setAmount (m_editItem->nerol_pct ()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::pinene_pct ) { this->lineEdit_pinene ->setAmount (m_editItem->pinene_pct ()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::polyphenols_pct ) { this->lineEdit_polyphenols ->setAmount (m_editItem->polyphenols_pct ()); if (propName) { return; } } + if (!propName || *propName == PropertyNames::Hop::xanthohumol_pct ) { this->lineEdit_xanthohumol ->setAmount (m_editItem->xanthohumol_pct ()); if (propName) { return; } } + + return; +} + +// Insert the boiler-plate stuff that we cannot do in EditorBase +EDITOR_COMMON_SLOT_DEFINITIONS(HopEditor) diff --git a/src/HopEditor.h b/src/editors/HopEditor.h similarity index 69% rename from src/HopEditor.h rename to src/editors/HopEditor.h index d9c81126..0f266416 100644 --- a/src/HopEditor.h +++ b/src/editors/HopEditor.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * HopEditor.h is part of Brewken, and is copyright the following authors 2009-2022: + * editors/HopEditor.h is part of Brewken, and is copyright the following authors 2009-2023: * • Jeff Bailey * • Mik Firestone * • Philip Greggory Lee @@ -15,14 +15,16 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#ifndef HOPEDITOR_H -#define HOPEDITOR_H +#ifndef EDITORS_HOPEDITOR_H +#define EDITORS_HOPEDITOR_H #pragma once #include "ui_hopEditor.h" #include #include +#include "editors/EditorBase.h" + // Forward declarations. class Hop; @@ -31,32 +33,23 @@ class Hop; * * \brief View/controller class for modifying hops. */ -class HopEditor : public QDialog, private Ui::hopEditor { +///class HopEditor : public QDialog, private Ui::hopEditor { +class HopEditor : public QDialog, private Ui::hopEditor, public EditorBase { Q_OBJECT public: HopEditor(QWidget * parent = nullptr); virtual ~HopEditor(); - //! Edit the given hop. - void setHop(Hop * h); - //! Create a new hop - void newHop(QString folder); + + void writeFieldsToEditItem(); + void writeLateFieldsToEditItem(); + void readFieldsFromEditItem(std::optional propName); public slots: - //! Save the changes. void save(); - //! Clear the dialog and close it. void clearAndClose(); - void changed(QMetaProperty,QVariant); - void clickedNewHop(); - -private: - Hop* obsHop; - - /*! Updates the UI elements based on \b prop. - * If null, updates all UI elements. - */ - void showChanges(QMetaProperty* prop = nullptr); + void changed(QMetaProperty, QVariant); + void clickedNew(); }; #endif diff --git a/src/MashEditor.cpp b/src/editors/MashEditor.cpp similarity index 98% rename from src/MashEditor.cpp rename to src/editors/MashEditor.cpp index b0b10123..cdb85c5d 100644 --- a/src/MashEditor.cpp +++ b/src/editors/MashEditor.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * MashEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: + * editors/MashEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Kregg Kemper * • Matt Young @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#include "MashEditor.h" +#include "editors/MashEditor.h" #include #include diff --git a/src/MashEditor.h b/src/editors/MashEditor.h similarity index 92% rename from src/MashEditor.h rename to src/editors/MashEditor.h index 5a0de8ff..96a19e1c 100644 --- a/src/MashEditor.h +++ b/src/editors/MashEditor.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * MashEditor.h is part of Brewken, and is copyright the following authors 2009-2021: + * editors/MashEditor.h is part of Brewken, and is copyright the following authors 2009-2021: * • Matt Young * • Mik Firestone * • Philip Greggory Lee @@ -15,8 +15,8 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#ifndef MASHEDITOR_H -#define MASHEDITOR_H +#ifndef EDITORS_MASHEDITOR_H +#define EDITORS_MASHEDITOR_H #pragma once #include diff --git a/src/MashStepEditor.cpp b/src/editors/MashStepEditor.cpp similarity index 98% rename from src/MashStepEditor.cpp rename to src/editors/MashStepEditor.cpp index e623af6b..011f6e06 100644 --- a/src/MashStepEditor.cpp +++ b/src/editors/MashStepEditor.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * MashStepEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: + * editors/MashStepEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Matt Young * • Mik Firestone @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#include "MashStepEditor.h" +#include "editors/MashStepEditor.h" #include "MainWindow.h" #include "measurement/Unit.h" diff --git a/src/MashStepEditor.h b/src/editors/MashStepEditor.h similarity index 93% rename from src/MashStepEditor.h rename to src/editors/MashStepEditor.h index 2835f0ed..970fa062 100644 --- a/src/MashStepEditor.h +++ b/src/editors/MashStepEditor.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * MashStepEditor.h is part of Brewken, and is copyright the following authors 2009-2020: + * editors/MashStepEditor.h is part of Brewken, and is copyright the following authors 2009-2020: * • Jeff Bailey * • Matt Young * • Mik Firestone @@ -16,8 +16,8 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#ifndef MASHSTEPEDITOR_H -#define MASHSTEPEDITOR_H +#ifndef EDITORS_MASHSTEPEDITOR_H +#define EDITORS_MASHSTEPEDITOR_H #pragma once #include diff --git a/src/MiscEditor.cpp b/src/editors/MiscEditor.cpp similarity index 98% rename from src/MiscEditor.cpp rename to src/editors/MiscEditor.cpp index 6f10d633..2e05e1bf 100644 --- a/src/MiscEditor.cpp +++ b/src/editors/MiscEditor.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * MiscEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: + * editors/MiscEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Matt Young * • Mik Firestone @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#include "MiscEditor.h" +#include "editors/MiscEditor.h" #include #include diff --git a/src/MiscEditor.h b/src/editors/MiscEditor.h similarity index 93% rename from src/MiscEditor.h rename to src/editors/MiscEditor.h index f6b7ea65..7827d875 100644 --- a/src/MiscEditor.h +++ b/src/editors/MiscEditor.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * MiscEditor.h is part of Brewken, and is copyright the following authors 2009-2023: + * editors/MiscEditor.h is part of Brewken, and is copyright the following authors 2009-2023: * • Jeff Bailey * • Matt Young * • Mik Firestone @@ -16,8 +16,8 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#ifndef MISCEDITOR_H -#define MISCEDITOR_H +#ifndef EDITORS_MISCEDITOR_H +#define EDITORS_MISCEDITOR_H #pragma once #include "ui_miscEditor.h" diff --git a/src/NamedMashEditor.cpp b/src/editors/NamedMashEditor.cpp similarity index 98% rename from src/NamedMashEditor.cpp rename to src/editors/NamedMashEditor.cpp index 91d1307b..92282218 100644 --- a/src/NamedMashEditor.cpp +++ b/src/editors/NamedMashEditor.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * NamedMashEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: + * editors/NamedMashEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Daniel Moreno * • Matt Young @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#include "NamedMashEditor.h" +#include "editors/NamedMashEditor.h" #include #include diff --git a/src/NamedMashEditor.h b/src/editors/NamedMashEditor.h similarity index 94% rename from src/NamedMashEditor.h rename to src/editors/NamedMashEditor.h index 9093b650..95d9027a 100644 --- a/src/NamedMashEditor.h +++ b/src/editors/NamedMashEditor.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * NamedMashEditor.h is part of Brewken, and is copyright the following authors 2009-2022: + * editors/NamedMashEditor.h is part of Brewken, and is copyright the following authors 2009-2022: * • Matt Young * • Mik Firestone * • Philip Greggory Lee @@ -15,8 +15,8 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#ifndef NAMEDMASHEDITOR_H -#define NAMEDMASHEDITOR_H +#ifndef EDITORS_NAMEDMASHEDITOR_H +#define EDITORS_NAMEDMASHEDITOR_H #pragma once #include @@ -27,7 +27,7 @@ #include "ui_namedMashEditor.h" #include "MashListModel.h" -#include "MashStepEditor.h" +#include "editors/MashStepEditor.h" #include "EquipmentListModel.h" #include "NamedEntitySortProxyModel.h" #include "tableModels/MashStepTableModel.h" diff --git a/src/StyleEditor.cpp b/src/editors/StyleEditor.cpp similarity index 99% rename from src/StyleEditor.cpp rename to src/editors/StyleEditor.cpp index dccd8b32..06f9a1aa 100644 --- a/src/StyleEditor.cpp +++ b/src/editors/StyleEditor.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * StyleEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: + * editors/StyleEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Matt Young * • Mik Firestone @@ -16,7 +16,7 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#include "StyleEditor.h" +#include "editors/StyleEditor.h" #include diff --git a/src/StyleEditor.h b/src/editors/StyleEditor.h similarity index 93% rename from src/StyleEditor.h rename to src/editors/StyleEditor.h index 28990206..659b7490 100644 --- a/src/StyleEditor.h +++ b/src/editors/StyleEditor.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * StyleEditor.h is part of Brewken, and is copyright the following authors 2009-2023: + * editors/StyleEditor.h is part of Brewken, and is copyright the following authors 2009-2023: * • Jeff Bailey * • Matt Young * • Mik Firestone @@ -16,8 +16,8 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#ifndef STYLEEDITOR_H -#define STYLEEDITOR_H +#ifndef EDITORS_STYLEEDITOR_H +#define EDITORS_STYLEEDITOR_H #pragma once #include diff --git a/src/WaterEditor.cpp b/src/editors/WaterEditor.cpp similarity index 99% rename from src/WaterEditor.cpp rename to src/editors/WaterEditor.cpp index 13b7d18e..0dcb6771 100644 --- a/src/WaterEditor.cpp +++ b/src/editors/WaterEditor.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * WaterEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: + * editors/WaterEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Jeff Bailey * • Matt Young @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#include "WaterEditor.h" +#include "editors/WaterEditor.h" #include #include diff --git a/src/WaterEditor.h b/src/editors/WaterEditor.h similarity index 93% rename from src/WaterEditor.h rename to src/editors/WaterEditor.h index bfd921cb..f7159d41 100644 --- a/src/WaterEditor.h +++ b/src/editors/WaterEditor.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * WaterEditor.h is part of Brewken, and is copyright the following authors 2009-2023: + * editors/WaterEditor.h is part of Brewken, and is copyright the following authors 2009-2023: * • Jeff Bailey * • Matt Young * • Mik Firestone @@ -16,8 +16,8 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#ifndef WATEREDITOR_H -#define WATEREDITOR_H +#ifndef EDITORS_WATEREDITOR_H +#define EDITORS_WATEREDITOR_H #pragma once #include // For PImpl diff --git a/src/YeastEditor.cpp b/src/editors/YeastEditor.cpp similarity index 98% rename from src/YeastEditor.cpp rename to src/editors/YeastEditor.cpp index 826adcf2..86086665 100644 --- a/src/YeastEditor.cpp +++ b/src/editors/YeastEditor.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * YeastEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: + * editors/YeastEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Kregg Kemper * • Matt Young @@ -18,7 +18,7 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#include "YeastEditor.h" +#include "editors/YeastEditor.h" #include diff --git a/src/YeastEditor.h b/src/editors/YeastEditor.h similarity index 91% rename from src/YeastEditor.h rename to src/editors/YeastEditor.h index 714f201b..58fdaac7 100644 --- a/src/YeastEditor.h +++ b/src/editors/YeastEditor.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * YeastEditor.h is part of Brewken, and is copyright the following authors 2009-2023: + * editors/YeastEditor.h is part of Brewken, and is copyright the following authors 2009-2023: * • Jeff Bailey * • Matt Young * • Mik Firestone @@ -16,8 +16,8 @@ * You should have received a copy of the GNU General Public License along with this program. If not, see * . =====================================================================================================================*/ -#ifndef YEASTEDITOR_H -#define YEASTEDITOR_H +#ifndef EDITORS_YEASTEDITOR_H +#define EDITORS_YEASTEDITOR_H #pragma once #include diff --git a/src/json/BeerJson.cpp b/src/json/BeerJson.cpp index 480f1f74..d936e399 100644 --- a/src/json/BeerJson.cpp +++ b/src/json/BeerJson.cpp @@ -386,7 +386,7 @@ namespace { {JsonRecordDefinition::FieldType::String, "producer", PropertyNames::Hop::producer, }, {JsonRecordDefinition::FieldType::String, "product_id", PropertyNames::Hop::product_id, }, {JsonRecordDefinition::FieldType::String, "origin", PropertyNames::Hop::origin, }, - {JsonRecordDefinition::FieldType::String, "year", PropertyNames::Hop::year, }, // Yes, year really is a string, not an integer in BeerJSON. .:TODO.JSON:. Decide int vs string **** + {JsonRecordDefinition::FieldType::String, "year", PropertyNames::Hop::year, }, {JsonRecordDefinition::FieldType::Enum, "form", PropertyNames::Hop::form, &Hop::formStringMapping}, }; std::initializer_list const BeerJson_HopType_ExclBase { diff --git a/src/json/JsonRecord.cpp b/src/json/JsonRecord.cpp index 7213cbdc..598a7d68 100644 --- a/src/json/JsonRecord.cpp +++ b/src/json/JsonRecord.cpp @@ -939,20 +939,8 @@ void JsonRecord::insertValue(JsonRecordDefinition::FieldDefinition const & field break; case JsonRecordDefinition::FieldType::String: - // - // Unless and until we get a fix for https://github.com/beerjson/beerjson/issues/196, there is a slightly - // ugly special case we have to handle where WE store Hop Year internally as an optional int but BeerJSON - // stores it as a string. - // - // .:TODO JSON:. Need to make PropertyNames::Hop::year optional and update UI to support this - if (*fieldDefinition.propertyName == PropertyNames::Hop::year) { - if (Optional::removeOptionalWrapperIfPresent(value, propertyIsOptional)) { - // QVariant knows how to convert an int to a QString so calling `value.toString()` is OK here - std::string yearAsString = value.toString().toStdString(); - recordDataAsObject.emplace(key, yearAsString); - } - break; - } + // We mostly don't bother making string fields optional because empty string will suffice for a string value + // that is not specified. But we keep the logic consistent here anyway. if (Optional::removeOptionalWrapperIfPresent(value, propertyIsOptional)) { std::string valueAsString = value.toString().toStdString(); diff --git a/src/model/Hop.cpp b/src/model/Hop.cpp index 5a6bf0cc..ab48bbf4 100644 --- a/src/model/Hop.cpp +++ b/src/model/Hop.cpp @@ -169,7 +169,7 @@ TypeLookup const Hop::typeLookup { // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::producer , Hop::m_producer , NonPhysicalQuantity::String ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::product_id , Hop::m_product_id , NonPhysicalQuantity::String ), - PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::year , Hop::m_year , NonPhysicalQuantity::Dimensionless), + PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::year , Hop::m_year , NonPhysicalQuantity::String ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::total_oil_ml_per_100g, Hop::m_total_oil_ml_per_100g, NonPhysicalQuantity::Dimensionless), // Not really dimensionless... PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::farnesene_pct , Hop::m_farnesene_pct , NonPhysicalQuantity::Percentage ), PROPERTY_TYPE_LOOKUP_ENTRY(PropertyNames::Hop::geraniol_pct , Hop::m_geraniol_pct , NonPhysicalQuantity::Percentage ), @@ -206,7 +206,7 @@ Hop::Hop(QString name) : // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ m_producer {"" }, m_product_id {"" }, - m_year {std::nullopt}, + m_year {"" }, m_total_oil_ml_per_100g{std::nullopt}, m_farnesene_pct {std::nullopt}, m_geraniol_pct {std::nullopt}, @@ -240,7 +240,7 @@ Hop::Hop(NamedParameterBundle const & namedParameterBundle) : // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ m_producer {namedParameterBundle.val(PropertyNames::Hop::producer )}, m_product_id {namedParameterBundle.val(PropertyNames::Hop::product_id )}, - m_year {namedParameterBundle.val >(PropertyNames::Hop::year )}, + m_year {namedParameterBundle.val(PropertyNames::Hop::year )}, m_total_oil_ml_per_100g{namedParameterBundle.val>(PropertyNames::Hop::total_oil_ml_per_100g)}, m_farnesene_pct {namedParameterBundle.val>(PropertyNames::Hop::farnesene_pct )}, m_geraniol_pct {namedParameterBundle.val>(PropertyNames::Hop::geraniol_pct )}, @@ -309,7 +309,7 @@ double Hop::myrcene_pct() const { return this->m_ // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ QString Hop::producer() const { return this->m_producer; } QString Hop::product_id() const { return this->m_product_id; } -std::optional Hop::year() const { return this->m_year; } +QString Hop::year() const { return this->m_year; } std::optional Hop::total_oil_ml_per_100g() const { return this->m_total_oil_ml_per_100g; } std::optional Hop::farnesene_pct() const { return this->m_farnesene_pct; } std::optional Hop::geraniol_pct() const { return this->m_geraniol_pct; } @@ -344,7 +344,7 @@ void Hop::setMyrcene_pct (double const val) { th // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ void Hop::setProducer (QString const & val) { this->setAndNotify(PropertyNames::Hop::producer, this->m_producer, val ); } void Hop::setProduct_id (QString const & val) { this->setAndNotify(PropertyNames::Hop::product_id, this->m_product_id, val ); } -void Hop::setYear (std::optional const val) { this->setAndNotify(PropertyNames::Hop::year, this->m_year, val ); } +void Hop::setYear (QString const val) { this->setAndNotify(PropertyNames::Hop::year, this->m_year, val ); } void Hop::setTotal_oil_ml_per_100g(std::optional const val) { this->setAndNotify(PropertyNames::Hop::total_oil_ml_per_100g, this->m_total_oil_ml_per_100g, this->enforceMinAndMax(val, "total_oil_ml_per_100g", 0.0, 100.0)); } void Hop::setFarnesene_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::farnesene_pct, this->m_farnesene_pct, this->enforceMinAndMax(val, "farnesene_pct", 0.0, 100.0)); } void Hop::setGeraniol_pct (std::optional const val) { this->setAndNotify(PropertyNames::Hop::geraniol_pct, this->m_geraniol_pct, this->enforceMinAndMax(val, "geraniol_pct", 0.0, 100.0)); } diff --git a/src/model/Hop.h b/src/model/Hop.h index ffe83125..5e4934c9 100644 --- a/src/model/Hop.h +++ b/src/model/Hop.h @@ -225,7 +225,12 @@ class Hop : public NamedEntityWithInventory { // .:TODO JSON:. Some of these should be optional Q_PROPERTY(QString producer READ producer WRITE setProducer ) Q_PROPERTY(QString product_id READ product_id WRITE setProduct_id ) - Q_PROPERTY(std::optional year READ year WRITE setYear ) + /** + * \brief It might seem odd to store year as a string rather than, say, std::optional, but this is + * deliberate and for two reasons. Firstly BeerJSON treats it as a string. Secondly, we don't want it + * formatted as a number when we display it. Nobody writes "2,023" or "2 023" for the year 2023. + */ + Q_PROPERTY(QString year READ year WRITE setYear ) Q_PROPERTY(std::optional total_oil_ml_per_100g READ total_oil_ml_per_100g WRITE setTotal_oil_ml_per_100g) Q_PROPERTY(std::optional farnesene_pct READ farnesene_pct WRITE setFarnesene_pct ) Q_PROPERTY(std::optional geraniol_pct READ geraniol_pct WRITE setGeraniol_pct ) @@ -256,7 +261,7 @@ class Hop : public NamedEntityWithInventory { // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ QString producer () const; QString product_id () const; - std::optional year () const; + QString year () const; std::optional total_oil_ml_per_100g() const; std::optional farnesene_pct () const; std::optional geraniol_pct () const; @@ -287,9 +292,9 @@ class Hop : public NamedEntityWithInventory { void setCohumulone_pct (double const val); void setMyrcene_pct (double const val); // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - void setProducer (QString const & val); - void setProduct_id (QString const & val); - void setYear (std::optional const val); + void setProducer (QString const & val); + void setProduct_id (QString const & val); + void setYear (QString const val); void setTotal_oil_ml_per_100g(std::optional const val); void setFarnesene_pct (std::optional const val); void setGeraniol_pct (std::optional const val); @@ -328,7 +333,7 @@ class Hop : public NamedEntityWithInventory { // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ QString m_producer; QString m_product_id; - std::optional m_year; + QString m_year; std::optional m_total_oil_ml_per_100g; std::optional m_farnesene_pct; std::optional m_geraniol_pct; diff --git a/src/model/Water.h b/src/model/Water.h index 13f1bc86..ceb10908 100644 --- a/src/model/Water.h +++ b/src/model/Water.h @@ -93,7 +93,7 @@ class Water : public NamedEntity { virtual ~Water(); - // It is useful to be able to assign one Water to another - see eg WaterEditor.cpp + // It is useful to be able to assign one Water to another - see eg editors/WaterEditor.cpp Water & operator=(Water other); protected: diff --git a/src/tableModels/BtTableModel.h b/src/tableModels/BtTableModel.h index cf9efd2a..3799a37c 100644 --- a/src/tableModels/BtTableModel.h +++ b/src/tableModels/BtTableModel.h @@ -17,6 +17,7 @@ #define TABLEMODELS_BTTABLEMODEL_H #pragma once +#include #include #include diff --git a/src/widgets/SmartAmounts.h b/src/widgets/SmartAmounts.h index 52c39af6..48817bd8 100644 --- a/src/widgets/SmartAmounts.h +++ b/src/widgets/SmartAmounts.h @@ -168,7 +168,7 @@ namespace SmartAmounts { * * SMART_FIELD_INIT(FermentableEditor, label_color, field_color, Fermentable, PropertyNames::Fermentable::color_srm, 0); * - * \param editorClass The class name of the class holding the field we're initialising, eg \c HopEditor. (In theory we + * \param editorClass The class name of the class holding the field we're initialising, eg \c editors/HopEditor. (In theory we * could pick this up via \c staticMetaObject.className(), but then we wouldn't be able to do the * macro concatenation here.) * \param modelClass The subclass of \c NamedEntity that we're editing. Eg in \c HopEditor, this will be \c Hop diff --git a/src/xml/BeerXml.cpp b/src/xml/BeerXml.cpp index 743817ec..1dad160f 100644 --- a/src/xml/BeerXml.cpp +++ b/src/xml/BeerXml.cpp @@ -127,10 +127,10 @@ namespace { {XmlRecord::FieldType::String, "DISPLAY_AMOUNT", BtString::NULL_STR, nullptr}, // Extension tag {XmlRecord::FieldType::String, "INVENTORY", BtString::NULL_STR, nullptr}, // Extension tag {XmlRecord::FieldType::String, "DISPLAY_TIME", BtString::NULL_STR, nullptr}, // Extension tag - // Following are new fields that BeerJSON adds to BeerXML, so all extension tags in BeerXML + // ⮜⮜⮜ Following are new fields that BeerJSON adds to BeerXML, so all extension tags in BeerXML ⮞⮞⮞ {XmlRecord::FieldType::String, "PRODUCER", PropertyNames::Hop::producer }, {XmlRecord::FieldType::String, "PRODUCT_ID", PropertyNames::Hop::product_id }, - {XmlRecord::FieldType::Int, "YEAR", PropertyNames::Hop::year }, + {XmlRecord::FieldType::String, "YEAR", PropertyNames::Hop::year }, {XmlRecord::FieldType::Double, "TOTAL_OIL_ML_PER_100G", PropertyNames::Hop::total_oil_ml_per_100g }, {XmlRecord::FieldType::Double, "FARNESENE", PropertyNames::Hop::farnesene_pct }, {XmlRecord::FieldType::Double, "GERANIOL", PropertyNames::Hop::geraniol_pct }, @@ -161,32 +161,56 @@ namespace { {"Adjunct", Fermentable::Type::Honey}, }; template<> XmlRecord::FieldDefinitions const BEER_XML_RECORD_FIELDS { - // Type XPath Q_PROPERTY Enum Mapper - {XmlRecord::FieldType::String, "NAME", PropertyNames::NamedEntity::name, nullptr}, - {XmlRecord::FieldType::RequiredConstant, "VERSION", VERSION1, nullptr}, - {XmlRecord::FieldType::Enum, "TYPE", PropertyNames::Fermentable::type, &BEER_XML_FERMENTABLE_TYPE_MAPPER}, - {XmlRecord::FieldType::Double, "AMOUNT", PropertyNames::Fermentable::amount, nullptr}, - {XmlRecord::FieldType::Double, "YIELD", PropertyNames::Fermentable::yield_pct, nullptr}, - {XmlRecord::FieldType::Double, "COLOR", PropertyNames::Fermentable::color_srm, nullptr}, - {XmlRecord::FieldType::Bool, "ADD_AFTER_BOIL", PropertyNames::Fermentable::addAfterBoil, nullptr}, - {XmlRecord::FieldType::String, "ORIGIN", PropertyNames::Fermentable::origin, nullptr}, - {XmlRecord::FieldType::String, "SUPPLIER", PropertyNames::Fermentable::supplier, nullptr}, - {XmlRecord::FieldType::String, "NOTES", PropertyNames::Fermentable::notes, nullptr}, - {XmlRecord::FieldType::Double, "COARSE_FINE_DIFF", PropertyNames::Fermentable::coarseFineDiff_pct, nullptr}, - {XmlRecord::FieldType::Double, "MOISTURE", PropertyNames::Fermentable::moisture_pct, nullptr}, - {XmlRecord::FieldType::Double, "DIASTATIC_POWER", PropertyNames::Fermentable::diastaticPower_lintner, nullptr}, - {XmlRecord::FieldType::Double, "PROTEIN", PropertyNames::Fermentable::protein_pct, nullptr}, - {XmlRecord::FieldType::Double, "MAX_IN_BATCH", PropertyNames::Fermentable::maxInBatch_pct, nullptr}, - {XmlRecord::FieldType::Bool, "RECOMMEND_MASH", PropertyNames::Fermentable::recommendMash, nullptr}, - {XmlRecord::FieldType::Double, "IBU_GAL_PER_LB", PropertyNames::Fermentable::ibuGalPerLb, nullptr}, - {XmlRecord::FieldType::String, "DISPLAY_AMOUNT", BtString::NULL_STR, nullptr}, // Extension tag - {XmlRecord::FieldType::String, "POTENTIAL", BtString::NULL_STR, nullptr}, // Extension tag - {XmlRecord::FieldType::String, "INVENTORY", BtString::NULL_STR, nullptr}, // Extension tag - {XmlRecord::FieldType::String, "DISPLAY_COLOR", BtString::NULL_STR, nullptr}, // Extension tag - {XmlRecord::FieldType::Bool, "IS_MASHED", PropertyNames::Fermentable::isMashed, nullptr}, // Non-standard tag, not part of BeerXML 1.0 standard - // Following are new fields that BeerJSON adds to BeerXML, so all extension tags in BeerXML .:TODO:. Add the rest here - {XmlRecord::FieldType::Enum, "GRAIN_GROUP", PropertyNames::Fermentable::grainGroup, &Fermentable::grainGroupStringMapping}, - {XmlRecord::FieldType::Bool, "AMOUNT_IS_WEIGHT", PropertyNames::Fermentable::amountIsWeight, nullptr}, + // Type XPath Q_PROPERTY Enum Mapper + {XmlRecord::FieldType::String , "NAME" , PropertyNames::NamedEntity::name }, + {XmlRecord::FieldType::RequiredConstant, "VERSION" , VERSION1 }, + {XmlRecord::FieldType::Enum , "TYPE" , PropertyNames::Fermentable::type , &BEER_XML_FERMENTABLE_TYPE_MAPPER}, + {XmlRecord::FieldType::Double , "AMOUNT" , PropertyNames::Fermentable::amount }, + {XmlRecord::FieldType::Double , "YIELD" , PropertyNames::Fermentable::yield_pct }, + {XmlRecord::FieldType::Double , "COLOR" , PropertyNames::Fermentable::color_srm }, + {XmlRecord::FieldType::Bool , "ADD_AFTER_BOIL" , PropertyNames::Fermentable::addAfterBoil }, + {XmlRecord::FieldType::String , "ORIGIN" , PropertyNames::Fermentable::origin }, + {XmlRecord::FieldType::String , "SUPPLIER" , PropertyNames::Fermentable::supplier }, + {XmlRecord::FieldType::String , "NOTES" , PropertyNames::Fermentable::notes }, + {XmlRecord::FieldType::Double , "COARSE_FINE_DIFF" , PropertyNames::Fermentable::coarseFineDiff_pct }, + {XmlRecord::FieldType::Double , "MOISTURE" , PropertyNames::Fermentable::moisture_pct }, + {XmlRecord::FieldType::Double , "DIASTATIC_POWER" , PropertyNames::Fermentable::diastaticPower_lintner }, + {XmlRecord::FieldType::Double , "PROTEIN" , PropertyNames::Fermentable::protein_pct }, + {XmlRecord::FieldType::Double , "MAX_IN_BATCH" , PropertyNames::Fermentable::maxInBatch_pct }, + {XmlRecord::FieldType::Bool , "RECOMMEND_MASH" , PropertyNames::Fermentable::recommendMash }, + {XmlRecord::FieldType::Double , "IBU_GAL_PER_LB" , PropertyNames::Fermentable::ibuGalPerLb }, + {XmlRecord::FieldType::String , "DISPLAY_AMOUNT" , BtString::NULL_STR }, // Extension tag + {XmlRecord::FieldType::String , "POTENTIAL" , BtString::NULL_STR }, // Extension tag + {XmlRecord::FieldType::String , "INVENTORY" , BtString::NULL_STR }, // Extension tag + {XmlRecord::FieldType::String , "DISPLAY_COLOR" , BtString::NULL_STR }, // Extension tag + {XmlRecord::FieldType::Bool , "IS_MASHED" , PropertyNames::Fermentable::isMashed }, // Non-standard tag, not part of BeerXML 1.0 standard + // ⮜⮜⮜ Following are new fields that BeerJSON adds to BeerXML, so all extension tags in BeerXML ⮞⮞⮞ + {XmlRecord::FieldType::Enum , "GRAIN_GROUP" , PropertyNames::Fermentable::grainGroup , &Fermentable::grainGroupStringMapping}, + {XmlRecord::FieldType::Bool , "AMOUNT_IS_WEIGHT" , PropertyNames::Fermentable::amountIsWeight }, + {XmlRecord::FieldType::String , "PRODUCER" , PropertyNames::Fermentable::producer }, + {XmlRecord::FieldType::String , "PRODUCT_ID" , PropertyNames::Fermentable::productId }, + {XmlRecord::FieldType::Double , "FINE_GRIND_YIELD" , PropertyNames::Fermentable::fineGrindYield_pct }, + {XmlRecord::FieldType::Double , "COARSE_GRIND_YIELD" , PropertyNames::Fermentable::coarseGrindYield_pct }, + {XmlRecord::FieldType::Double , "POTENTIAL_YIELD" , PropertyNames::Fermentable::potentialYield_sg }, + {XmlRecord::FieldType::Double , "ALPHA_AMYLASE" , PropertyNames::Fermentable::alphaAmylase_dextUnits }, + {XmlRecord::FieldType::Double , "KOLBACH_INDEX" , PropertyNames::Fermentable::kolbachIndex_pct }, + {XmlRecord::FieldType::Double , "HARDNESS_PRP_GLASSY" , PropertyNames::Fermentable::hardnessPrpGlassy_pct }, + {XmlRecord::FieldType::Double , "HARDNESS_PRP_HALF" , PropertyNames::Fermentable::hardnessPrpHalf_pct }, + {XmlRecord::FieldType::Double , "HARDNESS_PRP_MEALY" , PropertyNames::Fermentable::hardnessPrpMealy_pct }, + {XmlRecord::FieldType::Double , "KERNEL_SIZE_PRP_PLUMP" , PropertyNames::Fermentable::kernelSizePrpPlump_pct }, + {XmlRecord::FieldType::Double , "KERNEL_SIZE_PRP_THIN" , PropertyNames::Fermentable::kernelSizePrpThin_pct }, + {XmlRecord::FieldType::Double , "FRIABILITY" , PropertyNames::Fermentable::friability_pct }, + {XmlRecord::FieldType::Double , "DI" , PropertyNames::Fermentable::di_ph }, + {XmlRecord::FieldType::Double , "VISCOSITY" , PropertyNames::Fermentable::viscosity_cP }, + {XmlRecord::FieldType::Double , "DMS_P" , PropertyNames::Fermentable::dmsP }, + {XmlRecord::FieldType::Bool , "DMS_PIS_MASS_PER_VOLUME" , PropertyNames::Fermentable::dmsPIsMassPerVolume }, + {XmlRecord::FieldType::Double , "FAN" , PropertyNames::Fermentable::fan }, + {XmlRecord::FieldType::Bool , "FAN_IS_MASS_PER_VOLUME" , PropertyNames::Fermentable::fanIsMassPerVolume }, + {XmlRecord::FieldType::Double , "FERMENTABILITY" , PropertyNames::Fermentable::fermentability_pct }, + {XmlRecord::FieldType::Double , "BETA_GLUCAN" , PropertyNames::Fermentable::betaGlucan }, + {XmlRecord::FieldType::Bool , "BETA_GLUCAN_IS_MASS_PER_VOLUME", PropertyNames::Fermentable::betaGlucanIsMassPerVolume}, + + }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/translations/bt_ca.ts b/translations/bt_ca.ts index 17908797..07b061af 100644 --- a/translations/bt_ca.ts +++ b/translations/bt_ca.ts @@ -5561,6 +5561,34 @@ El volum final al primari és de %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + Marcar si la quantitat es mesura en Kg en comptes de L. + + + Amount is weight? + + + + Checked if the given amount is weight instead of volume + + + + x1 + + + + x2 + + + + x3 + + + + x4 + +
    hopEditor diff --git a/translations/bt_cs.ts b/translations/bt_cs.ts index 775bc4c6..e25b1650 100644 --- a/translations/bt_cs.ts +++ b/translations/bt_cs.ts @@ -5481,6 +5481,34 @@ Celkový objem pro hlavní kvašení je %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + Zaškrtněte, pokud se množství přísady vyjadřuje v kg místo l. + + + Amount is weight? + + + + Checked if the given amount is weight instead of volume + + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_de.ts b/translations/bt_de.ts index 4fa9bd32..fa71c054 100644 --- a/translations/bt_de.ts +++ b/translations/bt_de.ts @@ -5513,6 +5513,34 @@ Das endgültige Volumen in der Hauptgärung beträgt %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + Anhaken, wenn Menge in kg statt L angegeben wird. + + + Amount is weight? + Mengenangabe ist Gewicht + + + Checked if the given amount is weight instead of volume + Angehakt, wenn die Mengenangabe in Gewicht anstatt Volumen erfolgt + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_el.ts b/translations/bt_el.ts index 01b8c500..fc6afbc4 100644 --- a/translations/bt_el.ts +++ b/translations/bt_el.ts @@ -5483,6 +5483,34 @@ The final volume in the primary is %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + Τσέκαρε το εαν το ποσό που αναγράφεται είναι σε κιλά αντί για λίτρα + + + Amount is weight? + Το ποσό αναφέρεται σε βάρος; + + + Checked if the given amount is weight instead of volume + + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_en.ts b/translations/bt_en.ts index 666cd420..7e3be29f 100644 --- a/translations/bt_en.ts +++ b/translations/bt_en.ts @@ -4646,6 +4646,34 @@ The final volume in the primary is %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + + + + Amount is weight? + + + + Checked if the given amount is weight instead of volume + + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_es.ts b/translations/bt_es.ts index 69e4ac0a..a6001ede 100644 --- a/translations/bt_es.ts +++ b/translations/bt_es.ts @@ -5545,6 +5545,34 @@ El volumen final en el primario es %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + Marcar si la cantidad se mide por masa en vez de volumen + + + Amount is weight? + ¿Cantidad es masa? + + + Checked if the given amount is weight instead of volume + + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_et.ts b/translations/bt_et.ts index c222ebb1..33a69314 100644 --- a/translations/bt_et.ts +++ b/translations/bt_et.ts @@ -4704,6 +4704,34 @@ The final volume in the primary is %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + + + + Amount is weight? + + + + Checked if the given amount is weight instead of volume + + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_eu.ts b/translations/bt_eu.ts index 24dec40e..5741b2cb 100644 --- a/translations/bt_eu.ts +++ b/translations/bt_eu.ts @@ -4716,6 +4716,34 @@ The final volume in the primary is %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + + + + Amount is weight? + + + + Checked if the given amount is weight instead of volume + + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_fr.ts b/translations/bt_fr.ts index 23a72c80..d371cc16 100644 --- a/translations/bt_fr.ts +++ b/translations/bt_fr.ts @@ -5567,6 +5567,34 @@ Le volume final dans la cuve de fermentation est de %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + Vérifier que la quantité est exprimée en kg et non en L. + + + Amount is weight? + Exprimée en poids + + + Checked if the given amount is weight instead of volume + À cocher si la quantité est exprimée en poids et non en volume + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_gl.ts b/translations/bt_gl.ts index 7fce4a71..ff16b9a4 100644 --- a/translations/bt_gl.ts +++ b/translations/bt_gl.ts @@ -4850,6 +4850,34 @@ The final volume in the primary is %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + + + + Amount is weight? + + + + Checked if the given amount is weight instead of volume + + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_hu.ts b/translations/bt_hu.ts index c1b0ffbe..7d3c72d9 100644 --- a/translations/bt_hu.ts +++ b/translations/bt_hu.ts @@ -5509,6 +5509,34 @@ Végleges mennyiség az elsődleges erjesztőben: %1 Fermantable Type + + Check it if the amount listed is in kg instead of L. + + + + Amount is weight? + + + + Checked if the given amount is weight instead of volume + + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_it.ts b/translations/bt_it.ts index 65cd2515..b514d376 100644 --- a/translations/bt_it.ts +++ b/translations/bt_it.ts @@ -5565,6 +5565,34 @@ Il Volume finale del primo è %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + Controllare se l'importo indicato è in kg invece di L. + + + Amount is weight? + + + + Checked if the given amount is weight instead of volume + Controlla se la data quantità è peso invece di volume + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_lv.ts b/translations/bt_lv.ts index 9401acb0..550dafca 100644 --- a/translations/bt_lv.ts +++ b/translations/bt_lv.ts @@ -4783,6 +4783,34 @@ The final volume in the primary is %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + + + + Amount is weight? + + + + Checked if the given amount is weight instead of volume + + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_nb.ts b/translations/bt_nb.ts index bff5f635..cb0e4fe1 100644 --- a/translations/bt_nb.ts +++ b/translations/bt_nb.ts @@ -5475,6 +5475,34 @@ Sluttvolumet i primærgjæringskaret er %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + Sjekk etter om mengde listet opp er i kg i stedet for L. + + + Amount is weight? + Mengde er i vekt? + + + Checked if the given amount is weight instead of volume + + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_nl.ts b/translations/bt_nl.ts index 7b58755a..3f32e91b 100644 --- a/translations/bt_nl.ts +++ b/translations/bt_nl.ts @@ -5523,6 +5523,34 @@ Het uiteindelijke volume in de hoofdvergisting is %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + Controleer of de hoeveelheid vermeld staat in kg i.p.v. L. + + + Amount is weight? + Hoeveelheid is gewicht? + + + Checked if the given amount is weight instead of volume + + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_pl.ts b/translations/bt_pl.ts index 03c9ca67..62b32432 100644 --- a/translations/bt_pl.ts +++ b/translations/bt_pl.ts @@ -5475,6 +5475,34 @@ Końcowa pojemność w fermentorze wyniesie %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + Zaznacz jeśli ilość podana jest w kg a nie w L. + + + Amount is weight? + Czy ilość jest wagą? + + + Checked if the given amount is weight instead of volume + Zaznacz jeśli podana ilość jest wagą a nie objętością + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_pt.ts b/translations/bt_pt.ts index 9cd7bcb1..94aa9370 100644 --- a/translations/bt_pt.ts +++ b/translations/bt_pt.ts @@ -5509,6 +5509,34 @@ O volume final do fermentador primário é %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + Checar se a quantidade está em Kg em vez de L. + + + Amount is weight? + + + + Checked if the given amount is weight instead of volume + Verifique se a quantidade informada é um peso ao invés de um volume + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_ru.ts b/translations/bt_ru.ts index 72417c6c..d8a18a92 100644 --- a/translations/bt_ru.ts +++ b/translations/bt_ru.ts @@ -5541,6 +5541,34 @@ The final volume in the primary is %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + Включите, если количество задано в кг, а не в литрах. + + + Amount is weight? + Количество - вес? + + + Checked if the given amount is weight instead of volume + + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_sr.ts b/translations/bt_sr.ts index 295ff2f4..49524793 100644 --- a/translations/bt_sr.ts +++ b/translations/bt_sr.ts @@ -5200,6 +5200,34 @@ The final volume in the primary is %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + + + + Amount is weight? + + + + Checked if the given amount is weight instead of volume + + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_sv.ts b/translations/bt_sv.ts index 1edb8016..f49f095a 100644 --- a/translations/bt_sv.ts +++ b/translations/bt_sv.ts @@ -5525,6 +5525,34 @@ Primärens slutgiltiga volym är %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + Kontrollera om mängden är listad i kg istället för L. + + + Amount is weight? + Mängden är i vikt? + + + Checked if the given amount is weight instead of volume + Markera denna om värdet indikerar vikt istället för volym + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_tr.ts b/translations/bt_tr.ts index da0d2ef0..ce1d63c0 100644 --- a/translations/bt_tr.ts +++ b/translations/bt_tr.ts @@ -4705,6 +4705,34 @@ The final volume in the primary is %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + + + + Amount is weight? + + + + Checked if the given amount is weight instead of volume + + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/translations/bt_zh.ts b/translations/bt_zh.ts index 26f80f96..f42c6bba 100644 --- a/translations/bt_zh.ts +++ b/translations/bt_zh.ts @@ -5435,6 +5435,34 @@ The final volume in the primary is %1. Fermantable Type + + Check it if the amount listed is in kg instead of L. + 检查如果上市量(公斤)代替L。 + + + Amount is weight? + 金额重量?Amount is weight? + + + Checked if the given amount is weight instead of volume + 检查,如果给定的量是重量,而不是数量 + + + x1 + + + + x2 + + + + x3 + + + + x4 + + hopEditor diff --git a/ui/fermentableEditor.ui b/ui/fermentableEditor.ui index bf7b9526..49c77157 100644 --- a/ui/fermentableEditor.ui +++ b/ui/fermentableEditor.ui @@ -242,6 +242,41 @@ + + + + + 0 + 0 + + + + Check it if the amount listed is in kg instead of L. + + + Amount is weight? + + + checkBox_isWeight + + + + + + + + 0 + 0 + + + + Checked if the given amount is weight instead of volume + + + + + + @@ -567,7 +602,76 @@ + + + + x1 + + + lineEdit_x1 + + + + + + x1 + + + + + + + x2 + + + lineEdit_x2 + + + + + + + x2 + + + + + + + x3 + + + lineEdit_x3 + + + + + + + x3 + + + + + + + x4 + + + lineEdit_x4 + + + + + + + x4 + + + +¥¥¥ Keep going to x23! ¥¥¥ + Qt::Vertical From f7bdca415359f902a68924422ecb58c3b1171df0 Mon Sep 17 00:00:00 2001 From: Matt Young Date: Sun, 7 May 2023 01:50:44 +0200 Subject: [PATCH 29/42] FermentableEditor inherits from EditorBase, plus tidy-up enum mappings --- src/BtTreeItem.cpp | 20 +-- src/FermentableDialog.cpp | 6 +- src/MainWindow.cpp | 8 +- src/database/Database.cpp | 8 +- src/database/ObjectStoreTyped.cpp | 76 +++++------ src/editors/EditorBase.h | 45 +++++- src/editors/FermentableEditor.cpp | 174 ++++++++---------------- src/editors/FermentableEditor.h | 25 ++-- src/editors/HopEditor.cpp | 30 ++-- src/editors/HopEditor.h | 4 +- src/json/BeerJson.cpp | 82 +++++------ src/json/JsonRecordDefinition.cpp | 26 ++-- src/measurement/SystemOfMeasurement.cpp | 36 ++--- src/measurement/UnitSystem.cpp | 12 +- src/model/Fermentable.cpp | 62 ++++----- src/model/Fermentable.h | 2 +- src/model/Hop.cpp | 98 ++++++------- src/model/Hop.h | 22 +-- src/utils/EnumStringMapping.cpp | 38 ++++-- src/utils/EnumStringMapping.h | 36 +++-- src/xml/BeerXml.cpp | 118 ++++++++-------- translations/bt_ca.ts | 20 ++- translations/bt_cs.ts | 20 ++- translations/bt_de.ts | 20 ++- translations/bt_el.ts | 20 ++- translations/bt_en.ts | 38 ++---- translations/bt_es.ts | 20 ++- translations/bt_et.ts | 38 ++---- translations/bt_eu.ts | 38 ++---- translations/bt_fr.ts | 20 ++- translations/bt_gl.ts | 38 ++---- translations/bt_hu.ts | 20 ++- translations/bt_it.ts | 20 ++- translations/bt_lv.ts | 38 ++---- translations/bt_nb.ts | 20 ++- translations/bt_nl.ts | 20 ++- translations/bt_pl.ts | 20 ++- translations/bt_pt.ts | 20 ++- translations/bt_ru.ts | 20 ++- translations/bt_sr.ts | 20 ++- translations/bt_sv.ts | 20 ++- translations/bt_tr.ts | 38 ++---- translations/bt_zh.ts | 20 ++- 43 files changed, 737 insertions(+), 739 deletions(-) diff --git a/src/BtTreeItem.cpp b/src/BtTreeItem.cpp index ae828ee2..02204d9a 100644 --- a/src/BtTreeItem.cpp +++ b/src/BtTreeItem.cpp @@ -48,16 +48,16 @@ namespace { EnumStringMapping const itemTypeToName { - {"RECIPE" , BtTreeItem::Type::RECIPE }, - {"EQUIPMENT" , BtTreeItem::Type::EQUIPMENT }, - {"FERMENTABLE", BtTreeItem::Type::FERMENTABLE}, - {"HOP" , BtTreeItem::Type::HOP }, - {"MISC" , BtTreeItem::Type::MISC }, - {"YEAST" , BtTreeItem::Type::YEAST }, - {"BREWNOTE" , BtTreeItem::Type::BREWNOTE }, - {"STYLE" , BtTreeItem::Type::STYLE }, - {"FOLDER" , BtTreeItem::Type::FOLDER }, - {"WATER" , BtTreeItem::Type::WATER } + {BtTreeItem::Type::RECIPE , "RECIPE" }, + {BtTreeItem::Type::EQUIPMENT , "EQUIPMENT" }, + {BtTreeItem::Type::FERMENTABLE, "FERMENTABLE"}, + {BtTreeItem::Type::HOP , "HOP" }, + {BtTreeItem::Type::MISC , "MISC" }, + {BtTreeItem::Type::YEAST , "YEAST" }, + {BtTreeItem::Type::BREWNOTE , "BREWNOTE" }, + {BtTreeItem::Type::STYLE , "STYLE" }, + {BtTreeItem::Type::FOLDER , "FOLDER" }, + {BtTreeItem::Type::WATER , "WATER" }, }; } diff --git a/src/FermentableDialog.cpp b/src/FermentableDialog.cpp index b44df829..e7bdcb24 100644 --- a/src/FermentableDialog.cpp +++ b/src/FermentableDialog.cpp @@ -161,7 +161,7 @@ void FermentableDialog::editSelected() { QModelIndex translated = fermTableProxy->mapToSource(selected[0]); auto ferm = fermTableModel->getRow(translated.row()); - fermEdit->setFermentable(ferm.get()); + fermEdit->setEditItem(ferm); fermEdit->show(); return; } @@ -210,12 +210,12 @@ void FermentableDialog::newFermentable(QString folder) { return; } - Fermentable* ferm = new Fermentable(name); + auto ferm = std::make_shared(name); if (!folder.isEmpty()) { ferm->setFolder(folder); } - fermEdit->setFermentable(ferm); + fermEdit->setEditItem(ferm); fermEdit->show(); return; } diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 2f20e0c2..e36038e6 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -1013,7 +1013,7 @@ void MainWindow::treeActivated(const QModelIndex &index) { { Fermentable * ferm = active->getItem(index); if (ferm) { - fermEditor->setFermentable(ferm); + fermEditor->setEditItem(ObjectStoreWrapper::getSharedFromRaw(ferm)); fermEditor->show(); } } @@ -2018,11 +2018,13 @@ void MainWindow::removeSelectedFermentable() { void MainWindow::editSelectedFermentable() { Fermentable* f = selectedFermentable(); - if( f == nullptr ) + if (!f) { return; + } - fermEditor->setFermentable(f); + fermEditor->setEditItem(ObjectStoreWrapper::getSharedFromRaw(f)); fermEditor->show(); + return; } void MainWindow::editSelectedMisc() { diff --git a/src/database/Database.cpp b/src/database/Database.cpp index 937c2eca..8f8e9300 100644 --- a/src/database/Database.cpp +++ b/src/database/Database.cpp @@ -65,10 +65,10 @@ namespace { EnumStringMapping const dbTypeToName { - {Database::tr("NODB" ), Database::DbType::NODB }, - {Database::tr("SQLITE"), Database::DbType::SQLITE}, - {Database::tr("PGSQL" ), Database::DbType::PGSQL }, - {Database::tr("ALLDB" ), Database::DbType::ALLDB }, + {Database::DbType::NODB , Database::tr("NODB" )}, + {Database::DbType::SQLITE, Database::tr("SQLITE")}, + {Database::DbType::PGSQL , Database::tr("PGSQL" )}, + {Database::DbType::ALLDB , Database::tr("ALLDB" )}, }; // diff --git a/src/database/ObjectStoreTyped.cpp b/src/database/ObjectStoreTyped.cpp index ec28fbdd..1a4e84bf 100644 --- a/src/database/ObjectStoreTyped.cpp +++ b/src/database/ObjectStoreTyped.cpp @@ -287,11 +287,11 @@ namespace { // NB: MashSteps don't get folders, because they don't separate from their Mash /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// EnumStringMapping const MASH_STEP_TYPE_ENUM { - {"Infusion", MashStep::Type::Infusion}, - {"Temperature", MashStep::Type::Temperature}, - {"Decoction", MashStep::Type::Decoction}, - {"FlySparge", MashStep::Type::flySparge}, - {"BatchSparge", MashStep::Type::batchSparge} + {MashStep::Type::Infusion , "Infusion" }, + {MashStep::Type::Temperature, "Temperature"}, + {MashStep::Type::Decoction , "Decoction" }, + {MashStep::Type::flySparge , "FlySparge" }, + {MashStep::Type::batchSparge, "BatchSparge"} }; template<> ObjectStore::TableDefinition const PRIMARY_TABLE { "mashstep", @@ -332,19 +332,19 @@ namespace { // Database field mappings for Misc /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// EnumStringMapping const MISC_TYPE_ENUM { - {"Spice", Misc::Type::Spice}, - {"Fining", Misc::Type::Fining}, - {"Water Agent", Misc::Type::Water_Agent}, - {"Herb", Misc::Type::Herb}, - {"Flavor", Misc::Type::Flavor}, - {"Other", Misc::Type::Other} + {Misc::Type::Spice , "Spice" }, + {Misc::Type::Fining , "Fining" }, + {Misc::Type::Water_Agent, "Water Agent"}, + {Misc::Type::Herb , "Herb" }, + {Misc::Type::Flavor , "Flavor" }, + {Misc::Type::Other , "Other" } }; EnumStringMapping const MISC_USE_ENUM { - {"Boil", Misc::Use::Boil}, - {"Mash", Misc::Use::Mash}, - {"Primary", Misc::Use::Primary}, - {"Secondary", Misc::Use::Secondary}, - {"Bottling", Misc::Use::Bottling} + {Misc::Use::Boil , "Boil" }, + {Misc::Use::Mash , "Mash" }, + {Misc::Use::Primary , "Primary" }, + {Misc::Use::Secondary, "Secondary"}, + {Misc::Use::Bottling , "Bottling" } }; template<> ObjectStore::TableDefinition const PRIMARY_TABLE { "misc", @@ -402,12 +402,12 @@ namespace { // Database field mappings for Style /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// EnumStringMapping const STYLE_TYPE_ENUM { - {"Lager", Style::Type::Lager}, - {"Ale", Style::Type::Ale}, - {"Mead", Style::Type::Mead}, - {"Wheat", Style::Type::Wheat}, - {"Mixed", Style::Type::Mixed}, - {"Cider", Style::Type::Cider} + {Style::Type::Lager, "Lager"}, + {Style::Type::Ale , "Ale" }, + {Style::Type::Mead , "Mead" }, + {Style::Type::Wheat, "Wheat"}, + {Style::Type::Mixed, "Mixed"}, + {Style::Type::Cider, "Cider"}, }; template<> ObjectStore::TableDefinition const PRIMARY_TABLE