diff --git a/src/AlcoholTool.cpp b/src/AlcoholTool.cpp index cea684144..4c8144f44 100644 --- a/src/AlcoholTool.cpp +++ b/src/AlcoholTool.cpp @@ -1,6 +1,6 @@ /* * AlcoholTool.cpp is part of Brewtarget, and is Copyright the following - * authors 2009-2021 + * authors 2009-2023 * - Matt Young * - Ryan Hoobler * @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -32,6 +31,7 @@ #include "Algorithms.h" #include "BtLineEdit.h" +#include "Localization.h" #include "PersistentSettings.h" #include "measurement/SystemOfMeasurement.h" #include "widgets/ToggleSwitch.h" @@ -145,26 +145,26 @@ class AlcoholTool::impl { } void updateCalculatedFields() { - double og = this->input_og->toSI().quantity; - double fg = this->input_fg->toSI().quantity; + double og = this->input_og->toCanonical().quantity(); + double fg = this->input_fg->toCanonical().quantity(); if (this->enableAdvancedInputs->isChecked()) { // User wants temperature correction - double calibrationTempInC = this->input_calibration_temperature->toSI().quantity; - double ogReadTempInC = this->input_og_temperature->toSI().quantity; - double fgReadTempInC = this->input_fg_temperature->toSI().quantity; + double calibrationTempInC = this->input_calibration_temperature->toCanonical().quantity(); + double ogReadTempInC = this->input_og_temperature->toCanonical().quantity(); + double fgReadTempInC = this->input_fg_temperature->toCanonical().quantity(); if (0.0 == calibrationTempInC || 0.0 == ogReadTempInC) { og = 0.0; this->corrected_og->setText("? sg"); } else { og = Algorithms::correctSgForTemperature(og, ogReadTempInC, calibrationTempInC); - this->corrected_og->setText(QLocale().toString(og, 'f', 3).append(" sg")); + this->corrected_og->setText(Localization::getLocale().toString(og, 'f', 3).append(" sg")); } if (0.0 == calibrationTempInC || 0.0 == fgReadTempInC) { fg = 0.0; this->corrected_fg->setText("? sg"); } else { fg = Algorithms::correctSgForTemperature(fg, fgReadTempInC, calibrationTempInC); - this->corrected_fg->setText(QLocale().toString(fg, 'f', 3).append(" sg")); + this->corrected_fg->setText(Localization::getLocale().toString(fg, 'f', 3).append(" sg")); } } @@ -181,7 +181,7 @@ class AlcoholTool::impl { // So, if ABV is, say, 5.179% the call to QLocale::toString() below will correctly round it to 5.18% and the user // can decide whether to use 5.1% or 5.2% on labels etc. // - this->output_result->setText(QLocale().toString(abv, 'f', 2).append("%")); + this->output_result->setText(Localization::getLocale().toString(abv, 'f', 2).append("%")); return; } @@ -257,7 +257,7 @@ class AlcoholTool::impl { this->enableAdvancedInputs->isChecked(), PersistentSettings::Sections::alcoholTool); PersistentSettings::insert(hydrometerCalibrationTemperatureInC, - this->input_calibration_temperature->toSI().quantity, + this->input_calibration_temperature->toCanonical().quantity(), PersistentSettings::Sections::alcoholTool); return; } diff --git a/src/Algorithms.cpp b/src/Algorithms.cpp index 196159632..ccd46ee0e 100644 --- a/src/Algorithms.cpp +++ b/src/Algorithms.cpp @@ -587,8 +587,8 @@ double Algorithms::correctSgForTemperature(double measuredSg, double readingTemp // https://onlinelibrary.wiley.com/doi/pdf/10.1002/j.2050-0416.1970.tb03327.x for a rather old example.) Hence the // use of non-SI units -- because the people in question were working in Fahrenheit. // - double tr = Measurement::Units::fahrenheit.fromSI(readingTempInC); - double tc = Measurement::Units::fahrenheit.fromSI(calibrationTempInC); + double tr = Measurement::Units::fahrenheit.fromCanonical(readingTempInC); + double tc = Measurement::Units::fahrenheit.fromCanonical(calibrationTempInC); double correctedSg = measuredSg * ( (1.00130346 - 0.000134722124 * tr + 0.00000204052596 * intPow(tr,2) - 0.00000000232820948 * intPow(tr,3)) / diff --git a/src/BrewNoteWidget.cpp b/src/BrewNoteWidget.cpp index 24775c157..142999e5a 100644 --- a/src/BrewNoteWidget.cpp +++ b/src/BrewNoteWidget.cpp @@ -146,7 +146,7 @@ void BrewNoteWidget::updateSG() { return; } - this->bNoteObs->setSg(lineEdit_SG->toSI().quantity); + this->bNoteObs->setSg(lineEdit_SG->toCanonical().quantity()); return; } @@ -155,7 +155,7 @@ void BrewNoteWidget::updateVolumeIntoBK_l() { return; } - this->bNoteObs->setVolumeIntoBK_l(lineEdit_volIntoBK->toSI().quantity); + this->bNoteObs->setVolumeIntoBK_l(lineEdit_volIntoBK->toCanonical().quantity()); return; } @@ -164,7 +164,7 @@ void BrewNoteWidget::updateStrikeTemp_c() { return; } - this->bNoteObs->setStrikeTemp_c(lineEdit_strikeTemp->toSI().quantity); + this->bNoteObs->setStrikeTemp_c(lineEdit_strikeTemp->toCanonical().quantity()); return; } @@ -173,7 +173,7 @@ void BrewNoteWidget::updateMashFinTemp_c() { return; } - this->bNoteObs->setMashFinTemp_c(lineEdit_mashFinTemp->toSI().quantity); + this->bNoteObs->setMashFinTemp_c(lineEdit_mashFinTemp->toCanonical().quantity()); return; } @@ -182,7 +182,7 @@ void BrewNoteWidget::updateOG() { return; } - this->bNoteObs->setOg(lineEdit_OG->toSI().quantity); + this->bNoteObs->setOg(lineEdit_OG->toCanonical().quantity()); return; } @@ -191,7 +191,7 @@ void BrewNoteWidget::updatePostBoilVolume_l() { return; } - this->bNoteObs->setPostBoilVolume_l(lineEdit_postBoilVol->toSI().quantity); + this->bNoteObs->setPostBoilVolume_l(lineEdit_postBoilVol->toCanonical().quantity()); this->showChanges(); return; } @@ -201,7 +201,7 @@ void BrewNoteWidget::updateVolumeIntoFerm_l() { return; } - this->bNoteObs->setVolumeIntoFerm_l(lineEdit_volIntoFerm->toSI().quantity); + this->bNoteObs->setVolumeIntoFerm_l(lineEdit_volIntoFerm->toCanonical().quantity()); this->showChanges(); return; } @@ -211,7 +211,7 @@ void BrewNoteWidget::updatePitchTemp_c() { return; } - this->bNoteObs->setPitchTemp_c(lineEdit_pitchTemp->toSI().quantity); + this->bNoteObs->setPitchTemp_c(lineEdit_pitchTemp->toCanonical().quantity()); this->showChanges(); return; } @@ -221,7 +221,7 @@ void BrewNoteWidget::updateFG() { return; } - this->bNoteObs->setFg(lineEdit_FG->toSI().quantity); + this->bNoteObs->setFg(lineEdit_FG->toCanonical().quantity()); this->showChanges(); return; } @@ -231,7 +231,7 @@ void BrewNoteWidget::updateFinalVolume_l() { return; } - this->bNoteObs->setFinalVolume_l(lineEdit_finalVol->toSI().quantity); + this->bNoteObs->setFinalVolume_l(lineEdit_finalVol->toCanonical().quantity()); // this->showChanges(); return; } @@ -254,7 +254,8 @@ void BrewNoteWidget::updateNotes() { return; } -void BrewNoteWidget::changed(QMetaProperty /*prop*/, QVariant /*val*/) { +void BrewNoteWidget::changed([[maybe_unused]] QMetaProperty prop, + [[maybe_unused]] QVariant val) { if (this->sender() != this->bNoteObs) { return; } @@ -263,7 +264,7 @@ void BrewNoteWidget::changed(QMetaProperty /*prop*/, QVariant /*val*/) { return; } -void BrewNoteWidget::showChanges(QString field) { +void BrewNoteWidget::showChanges([[maybe_unused]] QString field) { if (this->bNoteObs == nullptr) { return; } @@ -296,6 +297,6 @@ void BrewNoteWidget::showChanges(QString field) { return; } -void BrewNoteWidget::focusOutEvent(QFocusEvent *e) { +void BrewNoteWidget::focusOutEvent([[maybe_unused]] QFocusEvent * e) { return; } diff --git a/src/BtDigitWidget.cpp b/src/BtDigitWidget.cpp index a8cd75b8a..8dcd036a8 100644 --- a/src/BtDigitWidget.cpp +++ b/src/BtDigitWidget.cpp @@ -23,7 +23,6 @@ #include #include -#include #include #include "Localization.h" @@ -138,7 +137,7 @@ void BtDigitWidget::display(QString str) { static bool converted; this->pimpl->m_lastNum = Localization::toDouble(str, &converted); - this->pimpl->m_lastPrec = str.length() - str.lastIndexOf(QLocale().decimalPoint()) - 1; + 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 { diff --git a/src/BtFieldType.cpp b/src/BtFieldType.cpp index f5f2e1179..8fd488eb4 100644 --- a/src/BtFieldType.cpp +++ b/src/BtFieldType.cpp @@ -24,10 +24,12 @@ namespace { EnumStringMapping const nonPhysicalQuantityToName { - {"Date" , NonPhysicalQuantity::Date }, - {"String" , NonPhysicalQuantity::String }, - {"Count" , NonPhysicalQuantity::Count }, - {"Percentage", NonPhysicalQuantity::Percentage} + {"Date" , NonPhysicalQuantity::Date }, + {"String" , NonPhysicalQuantity::String }, + {"Count" , NonPhysicalQuantity::Count }, + {"Percentage" , NonPhysicalQuantity::Percentage }, + {"Bool" , NonPhysicalQuantity::Bool }, + {"Dimensionless", NonPhysicalQuantity::Dimensionless}, }; } diff --git a/src/BtFieldType.h b/src/BtFieldType.h index 0865ca59a..787056632 100644 --- a/src/BtFieldType.h +++ b/src/BtFieldType.h @@ -35,7 +35,14 @@ enum class NonPhysicalQuantity { Date, String, Count, - Percentage + Percentage, + 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. + */ + Dimensionless, }; /** diff --git a/src/BtLabel.cpp b/src/BtLabel.cpp index f511ec892..4c3acc278 100644 --- a/src/BtLabel.cpp +++ b/src/BtLabel.cpp @@ -28,7 +28,7 @@ #include "model/Style.h" #include "model/Recipe.h" #include "PersistentSettings.h" -#include "utils/OptionalToStream.h" +#include "utils/OptionalHelpers.h" #include "widgets/UnitAndScalePopUpMenu.h" BtLabel::BtLabel(QWidget *parent, @@ -43,12 +43,12 @@ BtLabel::BtLabel(QWidget *parent, BtLabel::~BtLabel() = default; -void BtLabel::enterEvent(QEvent* event) { +void BtLabel::enterEvent([[maybe_unused]] QEvent * event) { this->textEffect(true); return; } -void BtLabel::leaveEvent(QEvent* event) { +void BtLabel::leaveEvent([[maybe_unused]] QEvent * event) { this->textEffect(false); return; } @@ -141,9 +141,9 @@ void BtLabel::initializeMenu() { Measurement::PhysicalQuantity physicalQuantity = std::get(this->fieldType); this->contextMenu = UnitAndScalePopUpMenu::create(this->btParent, - physicalQuantity, - forcedSystemOfMeasurement, - forcedRelativeScale); + physicalQuantity, + forcedSystemOfMeasurement, + forcedRelativeScale); return; } diff --git a/src/BtLineEdit.cpp b/src/BtLineEdit.cpp index 4d62b9a7d..4d0210626 100644 --- a/src/BtLineEdit.cpp +++ b/src/BtLineEdit.cpp @@ -1,6 +1,6 @@ /* * BtLineEdit.cpp is part of Brewtarget, and is Copyright the following - * authors 2009-2021: + * authors 2009-2023: * - Matt Young * - Mik Firestone * - Philip Greggory Lee @@ -33,7 +33,7 @@ #include "measurement/UnitSystem.h" #include "model/NamedEntity.h" #include "PersistentSettings.h" -#include "utils/OptionalToStream.h" +#include "utils/OptionalHelpers.h" namespace { int const min_text_size = 8; @@ -182,7 +182,9 @@ void BtLineEdit::setText(NamedEntity * element, int precision) { char const * const propertyName = this->editField.toLatin1().constData(); QVariant const propertyValue = element->property(propertyName); - qDebug() << Q_FUNC_INFO << "Read property" << propertyName << "of" << *element << "as" << propertyValue; + qDebug() << + Q_FUNC_INFO << "Read property" << this->editField << "(" << propertyName << ") of" << *element << "as" << + propertyValue; bool force = false; auto const myFieldType = this->getFieldType(); if (std::holds_alternative(myFieldType) && @@ -295,37 +297,22 @@ void BtLineEdit::setDisplaySize(bool recalculate) { return; } -BtGenericEdit::BtGenericEdit(QWidget *parent) : BtLineEdit(parent, NonPhysicalQuantity::String, nullptr) { - return; -} - -BtMassEdit::BtMassEdit(QWidget *parent) : BtLineEdit(parent, Measurement::PhysicalQuantity::Mass, &Measurement::Units::kilograms) { - return; -} - -BtVolumeEdit::BtVolumeEdit(QWidget *parent) : BtLineEdit(parent, Measurement::PhysicalQuantity::Volume, &Measurement::Units::liters) { - return; -} - -BtTemperatureEdit::BtTemperatureEdit(QWidget *parent) : BtLineEdit(parent, Measurement::PhysicalQuantity::Temperature, &Measurement::Units::celsius, 1) { - return; -} - -BtTimeEdit::BtTimeEdit(QWidget *parent) : BtLineEdit(parent, Measurement::PhysicalQuantity::Time, &Measurement::Units::minutes, 3) { - return; -} - -BtDensityEdit::BtDensityEdit(QWidget *parent) : BtLineEdit(parent, Measurement::PhysicalQuantity::Density, &Measurement::Units::sp_grav) { - return; -} - -BtColorEdit::BtColorEdit(QWidget *parent) : BtLineEdit(parent, Measurement::PhysicalQuantity::Color, &Measurement::Units::srm) { - return; -} - -BtStringEdit::BtStringEdit(QWidget *parent) : BtLineEdit(parent, NonPhysicalQuantity::String, nullptr) { - return; -} +BtGenericEdit ::BtGenericEdit (QWidget *parent) : BtLineEdit(parent, NonPhysicalQuantity::String , nullptr ) { return; } +BtMassEdit ::BtMassEdit (QWidget *parent) : BtLineEdit(parent, Measurement::PhysicalQuantity::Mass , &Measurement::Units::kilograms ) { return; } +BtVolumeEdit ::BtVolumeEdit (QWidget *parent) : BtLineEdit(parent, Measurement::PhysicalQuantity::Volume , &Measurement::Units::liters ) { return; } +BtTimeEdit ::BtTimeEdit (QWidget *parent) : BtLineEdit(parent, Measurement::PhysicalQuantity::Time , &Measurement::Units::minutes , 3) { return; } +BtTemperatureEdit ::BtTemperatureEdit (QWidget *parent) : BtLineEdit(parent, Measurement::PhysicalQuantity::Temperature , &Measurement::Units::celsius , 1) { return; } +BtColorEdit ::BtColorEdit (QWidget *parent) : BtLineEdit(parent, Measurement::PhysicalQuantity::Color , &Measurement::Units::srm ) { return; } +BtDensityEdit ::BtDensityEdit (QWidget *parent) : BtLineEdit(parent, Measurement::PhysicalQuantity::Density , &Measurement::Units::sp_grav ) { return; } +BtDiastaticPowerEdit::BtDiastaticPowerEdit(QWidget *parent) : BtLineEdit(parent, Measurement::PhysicalQuantity::DiastaticPower, &Measurement::Units::lintner ) { return; } +BtAcidityEdit ::BtAcidityEdit (QWidget *parent) : BtLineEdit(parent, Measurement::PhysicalQuantity::Acidity , &Measurement::Units::pH ) { return; } +BtBitternessEdit ::BtBitternessEdit (QWidget *parent) : BtLineEdit(parent, Measurement::PhysicalQuantity::Bitterness , &Measurement::Units::ibu ) { return; } +BtCarbonationEdit ::BtCarbonationEdit (QWidget *parent) : BtLineEdit(parent, Measurement::PhysicalQuantity::Carbonation , &Measurement::Units::carbonationVolumes ) { return; } +BtConcentrationEdit ::BtConcentrationEdit (QWidget *parent) : BtLineEdit(parent, Measurement::PhysicalQuantity::Concentration , &Measurement::Units::partsPerMillion ) { return; } +BtViscosityEdit ::BtViscosityEdit (QWidget *parent) : BtLineEdit(parent, Measurement::PhysicalQuantity::Viscosity , &Measurement::Units::centipoise ) { return; } +BtStringEdit ::BtStringEdit (QWidget *parent) : BtLineEdit(parent, NonPhysicalQuantity::String , nullptr ) { return; } +BtPercentageEdit ::BtPercentageEdit (QWidget *parent) : BtLineEdit(parent, NonPhysicalQuantity::Percentage , nullptr , 0) { return; } +BtDimensionlessEdit ::BtDimensionlessEdit (QWidget *parent) : BtLineEdit(parent, NonPhysicalQuantity::Dimensionless , nullptr , 3) { return; } BtMixedEdit::BtMixedEdit(QWidget *parent) : BtLineEdit(parent, Measurement::PhysicalQuantity::Mixed) { // This is probably pure evil I will later regret @@ -345,13 +332,3 @@ void BtMixedEdit::setIsWeight(bool state) { this->onLineChanged(); return; } - -BtDiastaticPowerEdit::BtDiastaticPowerEdit(QWidget *parent) : - BtLineEdit(parent, Measurement::PhysicalQuantity::DiastaticPower, &Measurement::Units::lintner) { - return; -} - -BtPercentageEdit::BtPercentageEdit(QWidget *parent) : - BtLineEdit(parent, NonPhysicalQuantity::Percentage, nullptr, 0) { - return; -} diff --git a/src/BtLineEdit.h b/src/BtLineEdit.h index c5b019d09..3023888a3 100644 --- a/src/BtLineEdit.h +++ b/src/BtLineEdit.h @@ -1,6 +1,6 @@ /* * BtLineEdit.h is part of Brewtarget, and is Copyright the following - * authors 2009-2021: + * authors 2009-2023: * - Matt Young * - Mik Firestone * - Philip Greggory Lee @@ -50,7 +50,7 @@ class NamedEntity; */ class BtLineEdit : public QLineEdit, public UiAmountWithUnits { Q_OBJECT -/// 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) @@ -116,16 +116,22 @@ public slots: // .:TODO:. We should change the inheritance hierarchy so that BtGenericEdit and BtStringEdit etc do not inherit from // UiAmountWithUnits. // -class BtGenericEdit : public BtLineEdit { Q_OBJECT public: BtGenericEdit(QWidget* parent); }; -class BtMassEdit : public BtLineEdit { Q_OBJECT public: BtMassEdit(QWidget* parent); }; -class BtVolumeEdit : public BtLineEdit { Q_OBJECT public: BtVolumeEdit(QWidget* parent); }; -class BtTemperatureEdit : public BtLineEdit { Q_OBJECT public: BtTemperatureEdit(QWidget* parent); }; -class BtTimeEdit : public BtLineEdit { Q_OBJECT public: BtTimeEdit(QWidget* parent); }; -class BtDensityEdit : public BtLineEdit { Q_OBJECT public: BtDensityEdit(QWidget* parent); }; -class BtColorEdit : public BtLineEdit { Q_OBJECT public: BtColorEdit(QWidget* parent); }; -class BtStringEdit : public BtLineEdit { Q_OBJECT public: BtStringEdit(QWidget* parent); }; +class BtGenericEdit : public BtLineEdit { Q_OBJECT public: BtGenericEdit (QWidget* parent); }; +class BtMassEdit : public BtLineEdit { Q_OBJECT public: BtMassEdit (QWidget* parent); }; +class BtVolumeEdit : public BtLineEdit { Q_OBJECT public: BtVolumeEdit (QWidget* parent); }; +class BtTimeEdit : public BtLineEdit { Q_OBJECT public: BtTimeEdit (QWidget* parent); }; +class BtTemperatureEdit : public BtLineEdit { Q_OBJECT public: BtTemperatureEdit (QWidget* parent); }; +class BtColorEdit : public BtLineEdit { Q_OBJECT public: BtColorEdit (QWidget* parent); }; +class BtDensityEdit : public BtLineEdit { Q_OBJECT public: BtDensityEdit (QWidget* parent); }; class BtDiastaticPowerEdit : public BtLineEdit { Q_OBJECT public: BtDiastaticPowerEdit(QWidget* parent); }; -class BtPercentageEdit : public BtLineEdit { Q_OBJECT public: BtPercentageEdit(QWidget* parent); }; +class BtAcidityEdit : public BtLineEdit { Q_OBJECT public: BtAcidityEdit (QWidget* parent); }; +class BtBitternessEdit : public BtLineEdit { Q_OBJECT public: BtBitternessEdit (QWidget* parent); }; +class BtCarbonationEdit : public BtLineEdit { Q_OBJECT public: BtCarbonationEdit (QWidget* parent); }; +class BtConcentrationEdit : public BtLineEdit { Q_OBJECT public: BtConcentrationEdit (QWidget* parent); }; +class BtViscosityEdit : public BtLineEdit { Q_OBJECT public: BtViscosityEdit (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); }; // mixed objects are a pain. class BtMixedEdit : public BtLineEdit { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0e69cfcd6..2e0949156 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -47,8 +47,8 @@ set(filesToCompile_cpp ${repoDir}/src/Application.cpp ${repoDir}/src/BeerColorWidget.cpp ${repoDir}/src/boiltime.cpp - ${repoDir}/src/BrewDayFormatter.cpp ${repoDir}/src/BrewDayScrollWidget.cpp + ${repoDir}/src/BrewDayFormatter.cpp ${repoDir}/src/BrewNoteWidget.cpp ${repoDir}/src/BtColor.cpp ${repoDir}/src/BtDatePopup.cpp diff --git a/src/EquipmentEditor.cpp b/src/EquipmentEditor.cpp index 41942b902..1b7bd83f8 100644 --- a/src/EquipmentEditor.cpp +++ b/src/EquipmentEditor.cpp @@ -170,7 +170,7 @@ void EquipmentEditor::save() { double grainAbs = Localization::toDouble(lineEdit_grainAbsorption->text(), Q_FUNC_INFO); - double ga_LKg = grainAbs * volumeUnit->toSI(1.0).quantity * weightUnit->fromSI(1.0); + double ga_LKg = grainAbs * volumeUnit->toCanonical(1.0).quantity() * weightUnit->fromCanonical(1.0); QString message,inform,describe; bool problems=false; @@ -181,12 +181,12 @@ void EquipmentEditor::save() { inform = QString("%1%2") .arg(tr("The following values are not set:")) .arg(QString("
    ")); - if ( qFuzzyCompare(lineEdit_tunVolume->toSI().quantity,0.0) ) { + if ( qFuzzyCompare(lineEdit_tunVolume->toCanonical().quantity(), 0.0) ) { problems = true; inform = inform + QString("
  • %1
  • ").arg(tr("mash tun volume (all-grain and BIAB only)")); } - if ( qFuzzyCompare(lineEdit_batchSize->toSI().quantity, 0.0) ) { + if ( qFuzzyCompare(lineEdit_batchSize->toCanonical().quantity(), 0.0) ) { problems = true; inform = inform + QString("
  • %1
  • ").arg(tr("batch size")); } @@ -214,21 +214,21 @@ void EquipmentEditor::save() { } this->obsEquip->setName( lineEdit_name->text() ); - this->obsEquip->setBoilSize_l( lineEdit_boilSize->toSI().quantity ); - this->obsEquip->setBatchSize_l( lineEdit_batchSize->toSI().quantity ); - this->obsEquip->setTunVolume_l( lineEdit_tunVolume->toSI().quantity ); + this->obsEquip->setBoilSize_l( lineEdit_boilSize->toCanonical().quantity() ); + this->obsEquip->setBatchSize_l( lineEdit_batchSize->toCanonical().quantity() ); + this->obsEquip->setTunVolume_l( lineEdit_tunVolume->toCanonical().quantity() ); - this->obsEquip->setTunWeight_kg( lineEdit_tunWeight->toSI().quantity ); + this->obsEquip->setTunWeight_kg( lineEdit_tunWeight->toCanonical().quantity() ); this->obsEquip->setTunSpecificHeat_calGC( lineEdit_tunSpecificHeat->toDoubleRaw() ); - this->obsEquip->setBoilTime_min( lineEdit_boilTime->toSI().quantity); - this->obsEquip->setEvapRate_lHr( lineEdit_evaporationRate->toSI().quantity ); - this->obsEquip->setTopUpKettle_l( lineEdit_topUpKettle->toSI().quantity ); - this->obsEquip->setTopUpWater_l( lineEdit_topUpWater->toSI().quantity ); - this->obsEquip->setTrubChillerLoss_l( lineEdit_trubChillerLoss->toSI().quantity ); - this->obsEquip->setLauterDeadspace_l( lineEdit_lauterDeadspace->toSI().quantity ); + this->obsEquip->setBoilTime_min( lineEdit_boilTime->toCanonical().quantity()); + this->obsEquip->setEvapRate_lHr( lineEdit_evaporationRate->toCanonical().quantity() ); + this->obsEquip->setTopUpKettle_l( lineEdit_topUpKettle->toCanonical().quantity() ); + this->obsEquip->setTopUpWater_l( lineEdit_topUpWater->toCanonical().quantity() ); + this->obsEquip->setTrubChillerLoss_l( lineEdit_trubChillerLoss->toCanonical().quantity() ); + this->obsEquip->setLauterDeadspace_l( lineEdit_lauterDeadspace->toCanonical().quantity() ); this->obsEquip->setGrainAbsorption_LKg( ga_LKg ); - this->obsEquip->setBoilingPoint_c( lineEdit_boilingPoint->toSI().quantity ); + this->obsEquip->setBoilingPoint_c( lineEdit_boilingPoint->toCanonical().quantity() ); this->obsEquip->setHopUtilization_pct( lineEdit_hopUtilization->toDoubleRaw() ); this->obsEquip->setNotes(textEdit_notes->toPlainText()); @@ -277,7 +277,7 @@ void EquipmentEditor::resetAbsorption() { Measurement::Unit const * weightUnit = nullptr; Measurement::Unit const * volumeUnit = nullptr; Measurement::getThicknessUnits(&volumeUnit, &weightUnit); - double gaCustomUnits = PhysicalConstants::grainAbsorption_Lkg * volumeUnit->fromSI(1.0) * weightUnit->toSI(1.0).quantity; + double gaCustomUnits = PhysicalConstants::grainAbsorption_Lkg * volumeUnit->fromCanonical(1.0) * weightUnit->toCanonical(1.0).quantity(); lineEdit_grainAbsorption->setText(gaCustomUnits); return; @@ -328,7 +328,7 @@ void EquipmentEditor::showChanges() { textEdit_notes->setText( e->notes() ); - double gaCustomUnits = e->grainAbsorption_LKg() * volumeUnit->fromSI(1.0) * weightUnit->toSI(1.0).quantity; + double gaCustomUnits = e->grainAbsorption_LKg() * volumeUnit->fromCanonical(1.0) * weightUnit->toCanonical(1.0).quantity(); lineEdit_grainAbsorption->setText(gaCustomUnits); lineEdit_boilingPoint->setText(e); @@ -352,18 +352,18 @@ void EquipmentEditor::updateCheckboxRecord() { lineEdit_boilSize->setText(bar); lineEdit_boilSize->setEnabled(false); } else { - lineEdit_boilSize->setText(lineEdit_batchSize->toSI().quantity); + lineEdit_boilSize->setText(lineEdit_batchSize->toCanonical().quantity()); lineEdit_boilSize->setEnabled(true); } return; } double EquipmentEditor::calcBatchSize() { - double size = lineEdit_batchSize->toSI().quantity; - double topUp = lineEdit_topUpWater->toSI().quantity; - double trubLoss = lineEdit_trubChillerLoss->toSI().quantity; - double evapRate = lineEdit_evaporationRate->toSI().quantity; - double time = lineEdit_boilTime->toSI().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(); return size - topUp + trubLoss + (time/60.0)*evapRate; } diff --git a/src/FermentableEditor.cpp b/src/FermentableEditor.cpp index 2acd1f373..59ad0069e 100644 --- a/src/FermentableEditor.cpp +++ b/src/FermentableEditor.cpp @@ -30,30 +30,30 @@ #include "measurement/Unit.h" #include "model/Fermentable.h" -FermentableEditor::FermentableEditor( QWidget* parent ) : +FermentableEditor::FermentableEditor(QWidget* parent) : QDialog(parent), obsFerm(nullptr) { setupUi(this); - this->tabWidget_editor->tabBar()->setStyle( new BtHorizontalTabs ); + this->tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs); connect( pushButton_new, SIGNAL( clicked() ), this, SLOT( newFermentable() ) ); - connect( pushButton_save, &QAbstractButton::clicked, this, &FermentableEditor::save ); - connect( pushButton_cancel, &QAbstractButton::clicked, this, &FermentableEditor::clearAndClose ); + connect(pushButton_save, &QAbstractButton::clicked, this, &FermentableEditor::save); + connect(pushButton_cancel, &QAbstractButton::clicked, this, &FermentableEditor::clearAndClose); return; } -void FermentableEditor::setFermentable( Fermentable* newFerm ) { - if(newFerm) { +FermentableEditor::~FermentableEditor() = default; + +void FermentableEditor::setFermentable(Fermentable * newFerm) { + if (newFerm) { obsFerm = newFerm; showChanges(); } return; } -void FermentableEditor::save() -{ - if( !obsFerm ) - { +void FermentableEditor::save() { + if (!obsFerm) { setVisible(false); return; } @@ -64,20 +64,20 @@ void FermentableEditor::save() // order as the combobox. obsFerm->setType( static_cast(comboBox_type->currentIndex()) ); - obsFerm->setYield_pct(lineEdit_yield->toSI().quantity); - obsFerm->setColor_srm(lineEdit_color->toSI().quantity); - obsFerm->setAddAfterBoil( (checkBox_addAfterBoil->checkState() == Qt::Checked)? true : false ); - obsFerm->setOrigin( lineEdit_origin->text() ); - obsFerm->setSupplier( lineEdit_supplier->text() ); - obsFerm->setCoarseFineDiff_pct( lineEdit_coarseFineDiff->toSI().quantity ); - obsFerm->setMoisture_pct( lineEdit_moisture->toSI().quantity ); - obsFerm->setDiastaticPower_lintner( lineEdit_diastaticPower->toSI().quantity ); - obsFerm->setProtein_pct( lineEdit_protein->toSI().quantity ); - obsFerm->setMaxInBatch_pct( lineEdit_maxInBatch->toSI().quantity ); - obsFerm->setRecommendMash( (checkBox_recommendMash->checkState() == Qt::Checked) ? true : false ); - obsFerm->setIsMashed( (checkBox_isMashed->checkState() == Qt::Checked) ? true : false ); - obsFerm->setIbuGalPerLb( lineEdit_ibuGalPerLb->toSI().quantity ); - obsFerm->setNotes( textEdit_notes->toPlainText() ); + obsFerm->setYield_pct(lineEdit_yield->toCanonical().quantity()); + obsFerm->setColor_srm(lineEdit_color->toCanonical().quantity()); + obsFerm->setAddAfterBoil((checkBox_addAfterBoil->checkState() == Qt::Checked)? true : false); + obsFerm->setOrigin(lineEdit_origin->text()); + obsFerm->setSupplier(lineEdit_supplier->text()); + obsFerm->setCoarseFineDiff_pct(lineEdit_coarseFineDiff->toCanonical().quantity()); + obsFerm->setMoisture_pct(lineEdit_moisture->toCanonical().quantity()); + obsFerm->setDiastaticPower_lintner(lineEdit_diastaticPower->toCanonical().quantity()); + obsFerm->setProtein_pct(lineEdit_protein->toCanonical().quantity()); + obsFerm->setMaxInBatch_pct(lineEdit_maxInBatch->toCanonical().quantity()); + obsFerm->setRecommendMash((checkBox_recommendMash->checkState() == Qt::Checked) ? true : false); + obsFerm->setIsMashed((checkBox_isMashed->checkState() == Qt::Checked) ? true : false); + obsFerm->setIbuGalPerLb(lineEdit_ibuGalPerLb->toCanonical().quantity()); + obsFerm->setNotes(textEdit_notes->toPlainText()); if (this->obsFerm->key() < 0) { ObjectStoreWrapper::insert(*this->obsFerm); @@ -85,7 +85,7 @@ void FermentableEditor::save() // Since inventory amount isn't really an attribute of the Fermentable, it's best to store it after we know the // Fermentable has a DB record. - this->obsFerm->setInventoryAmount(lineEdit_inventory->toSI().quantity); + this->obsFerm->setInventoryAmount(lineEdit_inventory->toCanonical().quantity()); setVisible(false); return; @@ -103,7 +103,7 @@ void FermentableEditor::showChanges(QMetaProperty* metaProp) { QString propName; bool updateAll = false; - if( metaProp == nullptr ) { + if (metaProp == nullptr) { updateAll = true; } else { propName = metaProp->name(); @@ -223,14 +223,14 @@ void FermentableEditor::showChanges(QMetaProperty* metaProp) { void FermentableEditor::newFermentable(QString folder) { QString name = QInputDialog::getText(this, tr("Fermentable name"), tr("Fermentable name:")); - if( name.isEmpty() ) { + if (name.isEmpty()) { return; } // .:TODO:. Change to shared_ptr as currently leads to memory leak in clearAndClose() Fermentable * f = new Fermentable(name); - if ( ! folder.isEmpty() ) { + if (! folder.isEmpty()) { f->setFolder(folder); } diff --git a/src/FermentableEditor.h b/src/FermentableEditor.h index dc9d714d3..72d17881b 100644 --- a/src/FermentableEditor.h +++ b/src/FermentableEditor.h @@ -1,6 +1,6 @@ /* * FermentableEditor.h is part of Brewtarget, and is Copyright the following - * authors 2009-2021 + * authors 2009-2023 * - Jeff Bailey * - Matt Young * - Mik Firestone @@ -42,7 +42,7 @@ class FermentableEditor : public QDialog, private Ui::fermentableEditor public: FermentableEditor( QWidget *parent=nullptr ); - virtual ~FermentableEditor() {} + virtual ~FermentableEditor(); void setFermentable( Fermentable* f ); void newFermentable( QString folder ); diff --git a/src/FermentableSortFilterProxyModel.cpp b/src/FermentableSortFilterProxyModel.cpp index 5cb306522..68e09902f 100644 --- a/src/FermentableSortFilterProxyModel.cpp +++ b/src/FermentableSortFilterProxyModel.cpp @@ -47,7 +47,7 @@ bool FermentableSortFilterProxyModel::lessThan(QModelIndex const & left, Measurement::qStringToSI(rightFermentable.toString(), Measurement::PhysicalQuantity::Mass)) { return getName(right) < getName(left); } else if (Measurement::qStringToSI(leftFermentable.toString(), - Measurement::PhysicalQuantity::Mass).quantity == 0.0 && + Measurement::PhysicalQuantity::Mass).quantity() == 0.0 && this->sortOrder() == Qt::AscendingOrder) { // Show non-zero entries first. return false; diff --git a/src/HopEditor.cpp b/src/HopEditor.cpp index 22d8d96c4..41d8dda7c 100644 --- a/src/HopEditor.cpp +++ b/src/HopEditor.cpp @@ -67,6 +67,8 @@ HopEditor::HopEditor(QWidget * parent) : return; } +HopEditor::~HopEditor() = default; + void HopEditor::setHop(Hop * h) { if (obsHop) { disconnect(obsHop, nullptr, this, nullptr); @@ -77,6 +79,7 @@ void HopEditor::setHop(Hop * h) { connect(obsHop, &NamedEntity::changed, this, &HopEditor::changed); showChanges(); } + return; } void HopEditor::save() { @@ -86,34 +89,33 @@ void HopEditor::save() { } this->obsHop->setName(lineEdit_name->text()); - this->obsHop->setAlpha_pct(lineEdit_alpha->toSI().quantity); - this->obsHop->setTime_min(lineEdit_time->toSI().quantity); + this->obsHop->setAlpha_pct(lineEdit_alpha->toCanonical().quantity()); + this->obsHop->setTime_min(lineEdit_time->toCanonical().quantity()); // // 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->setUse (Hop::useStringMapping.stringToEnum (comboBox_hopUse->currentData().toString())); 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())); - this->obsHop->setBeta_pct(lineEdit_beta->toSI().quantity); - this->obsHop->setHsi_pct(lineEdit_HSI->toSI().quantity); - this->obsHop->setOrigin(lineEdit_origin->text()); - this->obsHop->setHumulene_pct(lineEdit_humulene->toSI().quantity); - this->obsHop->setCaryophyllene_pct(lineEdit_caryophyllene->toSI().quantity); - this->obsHop->setCohumulone_pct(lineEdit_cohumulone->toSI().quantity); - this->obsHop->setMyrcene_pct(lineEdit_myrcene->toSI().quantity); - - this->obsHop->setSubstitutes(textEdit_substitutes->toPlainText()); - this->obsHop->setNotes(textEdit_notes->toPlainText()); + this->obsHop->setBeta_pct (lineEdit_beta ->toCanonical().quantity()); + this->obsHop->setHsi_pct (lineEdit_HSI ->toCanonical().quantity()); + this->obsHop->setOrigin (lineEdit_origin ->text() ); + this->obsHop->setHumulene_pct (lineEdit_humulene ->toCanonical().quantity()); + this->obsHop->setCaryophyllene_pct(lineEdit_caryophyllene->toCanonical().quantity()); + this->obsHop->setCohumulone_pct (lineEdit_cohumulone ->toCanonical().quantity()); + this->obsHop->setMyrcene_pct (lineEdit_myrcene ->toCanonical().quantity()); + this->obsHop->setSubstitutes (textEdit_substitutes ->toPlainText() ); + this->obsHop->setNotes (textEdit_notes ->toPlainText() ); 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->toSI().quantity); + this->obsHop->setInventoryAmount(lineEdit_inventory->toCanonical().quantity()); setVisible(false); return; } @@ -189,8 +191,7 @@ void HopEditor::showChanges(QMetaProperty * prop) { void HopEditor::newHop(QString folder) { - QString name = QInputDialog::getText(this, tr("Hop name"), - tr("Hop name:")); + QString name = QInputDialog::getText(this, tr("Hop name"), tr("Hop name:")); if (name.isEmpty()) { return; } diff --git a/src/HopEditor.h b/src/HopEditor.h index 9879c104d..b42eec30b 100644 --- a/src/HopEditor.h +++ b/src/HopEditor.h @@ -1,6 +1,6 @@ /* * HopEditor.h is part of Brewtarget, and is Copyright the following - * authors 2009-2021 + * authors 2009-2023 * - Jeff Bailey * - Matt Young * - Mik Firestone @@ -41,7 +41,7 @@ class HopEditor : public QDialog, private Ui::hopEditor public: HopEditor( QWidget *parent=nullptr ); - virtual ~HopEditor() {} + virtual ~HopEditor(); //! Edit the given hop. void setHop( Hop* h ); //! Create a new hop diff --git a/src/HopSortFilterProxyModel.cpp b/src/HopSortFilterProxyModel.cpp index 974bed5a7..5eb5e2613 100644 --- a/src/HopSortFilterProxyModel.cpp +++ b/src/HopSortFilterProxyModel.cpp @@ -48,7 +48,7 @@ bool HopSortFilterProxyModel::lessThan(QModelIndex const & left, } case HOPINVENTORYCOL: - if (Measurement::qStringToSI(leftHop.toString(), Measurement::PhysicalQuantity::Mass).quantity == 0.0 && + if (Measurement::qStringToSI(leftHop.toString(), Measurement::PhysicalQuantity::Mass).quantity() == 0.0 && this->sortOrder() == Qt::AscendingOrder) { return false; } diff --git a/src/HydrometerTool.cpp b/src/HydrometerTool.cpp index 73c169fab..ea347143d 100644 --- a/src/HydrometerTool.cpp +++ b/src/HydrometerTool.cpp @@ -127,32 +127,32 @@ void HydrometerTool::doLayout() { void HydrometerTool::retranslateUi() { setWindowTitle(tr("Hydrometer Tool")); - label_inputSg->setText(tr("SG Reading")); //TODO translation - label_inputTemp->setText(tr("Temperature")); //TODO translation - label_calibratedTemp->setText(tr("Hydrometer Calibration")); //TODO translation - label_outputSg->setText(tr("Adjust SG")); //TODO translation + 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")); #ifndef QT_NO_TOOLTIP - lineEdit_inputSg->setToolTip(tr("Measured gravity")); //TODO translate - lineEdit_inputTemp->setToolTip(tr("Temperature")); //TODO translate - lineEdit_outputSg->setToolTip(tr("Corrected gravity")); //TODO translate + lineEdit_inputSg->setToolTip(tr("Measured gravity")); + lineEdit_inputTemp->setToolTip(tr("Temperature")); + lineEdit_outputSg->setToolTip(tr("Corrected gravity")); #endif return; } void HydrometerTool::convert() { double correctedGravity = Algorithms::correctSgForTemperature( - lineEdit_inputSg->toSI().quantity, // measured gravity - lineEdit_inputTemp->toSI().quantity, // temperature at time of reading in Celsius - lineEdit_calibratedTemp->toSI().quantity // calibration temperature of hydrometer 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); return; } -void HydrometerTool::changeEvent(QEvent* event) { +void HydrometerTool::changeEvent(QEvent * event) { if (event->type() == QEvent::LanguageChange) { this->retranslateUi(); } diff --git a/src/Localization.cpp b/src/Localization.cpp index 183cb7158..c824bd30c 100644 --- a/src/Localization.cpp +++ b/src/Localization.cpp @@ -39,6 +39,7 @@ // Anonymous namespace for constants, global variables and functions used only in this file // namespace { + Localization::NumericDateFormat dateFormat = Localization::YearMonthDay; QString currentLanguage = "en"; @@ -46,6 +47,53 @@ namespace { QTranslator defaultTrans; QTranslator btTrans; + QLocale initSystemLocale() { + // + // At the moment, you need to manually edit the config file to set a forced locale (which is a step up from having + // to hard-code something and recompile). Potentially in future we'll allow this to be set via the UI. + // + // Per the Qt doco, the meaningful part of the specifier for a locale has the format + // "language[_script][_country]" or "C", where: + // + // - language is a lowercase, two-letter, ISO 639 language code (also some three-letter codes), + // - script is a titlecase, four-letter, ISO 15924 script code (eg Latn, Cyrl, Hebr) + // - country is an uppercase, two-letter, ISO 3166 country code (also "419" as defined by United Nations), + // + // The separator can be either underscore (the standard) or a minus sign (as used in Java). + // + // If the string violates the locale format, or language is not a valid ISO 639 code, the "C" locale is used + // instead. If country is not present, or is not a valid ISO 3166 code, the most appropriate country is chosen + // for the specified language. + // + // So, eg, to force French/France locale, add the following line to the [General] section of the + // xxxPersistentSettings.conf file: + // forcedLocale=fr_FR + // + if (PersistentSettings::contains(PersistentSettings::Names::forcedLocale)) { + QLocale forcedLocale = + QLocale(PersistentSettings::value(PersistentSettings::Names::forcedLocale, "").toString()); + // This probably isn't needed, but should force this locale into places where QLocale::system() is being called + // instead of Localization::getLocale(). Note that QLocale::setDefault() is not reentrant, but that's OK as + // we are guaranteed to be single-threaded here. + QLocale::setDefault(forcedLocale); + return forcedLocale; + } + return QLocale::system(); + } + +} + + +QLocale const & Localization::getLocale() { + // + // Note that we can't use std::call_once to initialise systemLocale because it runs too early, ie before + // PersistentSettings has been initialised. Using a static local variable is thread-safe and (at the cost of a very + // small runtime overhead) means that the variable will not be initialised until the first call of this function + // (which should be after PersistentSettings::initialise() has been called). + // + static QLocale systemLocale = initSystemLocale(); + + return systemLocale; } void Localization::setDateFormat(NumericDateFormat newDateFormat) { @@ -71,10 +119,8 @@ QString Localization::numericToStringDateFormat(NumericDateFormat numericDateFor } } -QString Localization::displayDate(QDate const& date ) -{ - QLocale loc(QLocale::system().name()); - return date.toString(loc.dateFormat(QLocale::ShortFormat)); +QString Localization::displayDate(QDate const& date) { + return date.toString(Localization::getLocale().dateFormat(QLocale::ShortFormat)); } QString Localization::displayDateUserFormated(QDate const & date) { @@ -103,12 +149,12 @@ QString const & Localization::getSystemLanguage() { // QLocale::name() is of the form language_country, // where 'language' is a lowercase 2-letter ISO 639-1 language code, // and 'country' is an uppercase 2-letter ISO 3166 country code. - return QLocale::system().name().split("_")[0]; + return Localization::getLocale().name().split("_")[0]; } bool Localization::hasUnits(QString qstr) { - QString decimal = QRegExp::escape(QLocale::system().decimalPoint()); - QString grouping = QRegExp::escape(QLocale::system().groupSeparator()); + QString decimal = QRegExp::escape(Localization::getLocale().decimalPoint()); + QString grouping = QRegExp::escape(Localization::getLocale().groupSeparator()); QRegExp amtUnit("((?:\\d+" + grouping + ")?\\d+(?:" + decimal + "\\d+)?|" + decimal + "\\d+)\\s*(\\w+)?"); amtUnit.indexIn(qstr); @@ -177,7 +223,7 @@ void Localization::loadTranslations() { } // Load translators. - defaultTrans.load("qt_" + QLocale::system().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); + defaultTrans.load("qt_" + Localization::getLocale().name(), QLibraryInfo::location(QLibraryInfo::TranslationsPath)); if (getCurrentLanguage().isEmpty()) { setLanguage(getSystemLanguage()); } diff --git a/src/Localization.h b/src/Localization.h index 975477368..379818db2 100644 --- a/src/Localization.h +++ b/src/Localization.h @@ -24,12 +24,19 @@ #pragma once #include +#include #include class BtStringConst; class NamedEntity; namespace Localization { + /** + * \brief Returns the locale to use for formatting numbers etc. Usually this is the same as \c QLocale::system(), + * but can be overridden for testing purposes. + */ + QLocale const & getLocale(); + /** * \brief Coding for the three main numeric date formats * See https://en.wikipedia.org/wiki/Date_format_by_country for which countries use which date formats diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 3c7d40577..e8394ae91 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -129,7 +129,7 @@ #include "UndoableAddOrRemove.h" #include "UndoableAddOrRemoveList.h" #include "utils/BtStringConst.h" -#include "utils/OptionalToStream.h" +#include "utils/OptionalHelpers.h" #include "WaterDialog.h" #include "WaterEditor.h" #include "WaterListModel.h" @@ -778,7 +778,7 @@ void MainWindow::restoreSavedState() { } else { auto firstRecipeWeFind = ObjectStoreTyped::getInstance().findFirstMatching( // This trivial lambda gives us the first recipe in the list, if there is one - [](std::shared_ptr obj) {return true;} + []([[maybe_unused]] std::shared_ptr obj) {return true;} ); if (firstRecipeWeFind) { key = firstRecipeWeFind.value()->key(); @@ -1780,7 +1780,7 @@ void MainWindow::updateRecipeBatchSize() { this->doOrRedoUpdate(*this->recipeObs, PropertyNames::Recipe::batchSize_l, - lineEdit_batchSize->toSI().quantity, + lineEdit_batchSize->toCanonical().quantity(), tr("Change Batch Size")); } @@ -1791,7 +1791,7 @@ void MainWindow::updateRecipeBoilSize() { this->doOrRedoUpdate(*this->recipeObs, PropertyNames::Recipe::boilSize_l, - lineEdit_boilSize->toSI().quantity, + lineEdit_boilSize->toCanonical().quantity(), tr("Change Boil Size")); } @@ -1801,7 +1801,7 @@ void MainWindow::updateRecipeBoilTime() { } Equipment* kit = recipeObs->equipment(); - double boilTime = Measurement::qStringToSI(lineEdit_boilTime->text(), Measurement::PhysicalQuantity::Time).quantity; + double boilTime = Measurement::qStringToSI(lineEdit_boilTime->text(), Measurement::PhysicalQuantity::Time).quantity(); // Here, we rely on a signal/slot connection to propagate the equipment changes to recipeObs->boilTime_min and maybe // recipeObs->boilSize_l @@ -1955,7 +1955,7 @@ void MainWindow::doOrRedoUpdate(QObject & updatee, BtStringConst const & propertyName, QVariant newValue, QString const & description, - QUndoCommand * parent) { + [[maybe_unused]] QUndoCommand * parent) { /// qDebug() << Q_FUNC_INFO << "Updating" << propertyName << "on" << updatee.metaObject()->className(); /// qDebug() << Q_FUNC_INFO << "this=" << static_cast(this); this->doOrRedoUpdate(new SimpleUndoableUpdate(updatee, propertyName, newValue, description)); @@ -2454,67 +2454,40 @@ void MainWindow::setTreeSelection(QModelIndex item) { } // reduces the inventory by the selected recipes -void MainWindow::reduceInventory(){ +void MainWindow::reduceInventory() { - QModelIndexList indexes = treeView_recipe->selectionModel()->selectedRows(); - - foreach(QModelIndex selected, indexes) { + for (QModelIndex selected : treeView_recipe->selectionModel()->selectedRows()) { Recipe* rec = treeView_recipe->getItem(selected); - if ( rec == nullptr ) { - //try the parent recipe + if (rec == nullptr) { + // Try the parent recipe rec = treeView_recipe->getItem(treeView_recipe->parent(selected)); - if ( rec == nullptr ) { + if (rec == nullptr) { continue; } } // Make sure everything is properly set and selected - if( rec != recipeObs ) { + if (rec != recipeObs) { setRecipe(rec); } - int i = 0; - //reduce fermentables - QList flist = rec->fermentables(); - if ( flist.size() > 0 ){ - for( i = 0; static_cast(i) < flist.size(); ++i ) { - double newVal=flist[i]->inventory() - flist[i]->amount_kg(); - newVal = (newVal < 0) ? 0 : newVal; - flist[i]->setInventoryAmount(newVal); - } - } - - //reduce misc - QList mlist = rec->miscs(); - if ( mlist.size() > 0 ) { - for( i = 0; static_cast(i) < mlist.size(); ++i ) { - double newVal=mlist[i]->inventory() - mlist[i]->amount(); - newVal = (newVal < 0) ? 0 : newVal; - mlist[i]->setInventoryAmount(newVal); - } - } - //reduce hops - QList hlist = rec->hops(); - if( hlist.size() > 0 ) { - for( i = 0; static_cast(i) < hlist.size(); ++i ) { - double newVal = hlist[i]->inventory() - hlist[i]->amount_kg(); - newVal = (newVal < 0) ? 0 : newVal; - hlist[i]->setInventoryAmount(newVal); - } - } - //reduce yeast - QList ylist = rec->yeasts(); - if(ylist.size() > 0){ - for( i = 0; static_cast(i) < ylist.size(); ++i ) - { - //Yeast inventory is done by quanta not amount - // .:TBD:. I think "quanta" is being used to mean "number of packets" or something - int newVal = ylist[i]->inventory() - 1; - newVal = (newVal < 0) ? 0 : newVal; - ylist[i]->setInventoryQuanta(newVal); - } - } + // + // Reduce fermentables, miscs, hops, yeasts + // + // Note that yeast inventory is done by "quanta" not amount, probably trying to indicate number of packets rather + // than weight. Of course, even though quanta implies an integer measurement, inventory() still returns a double + // so there's some heroic casting going on here. + // + // For fermentables and miscs, the amount can be either mass or volume, but we don't worry about which here as we + // assume that a given type of fermentable/misc is always measured in the same way. + // + for (auto ii : rec->fermentables()) { ii->setInventoryAmount(std::max( ii->inventory() - ii->amount_kg(), 0.0)); } + for (auto ii : rec->miscs ()) { ii->setInventoryAmount(std::max( ii->inventory() - ii->amount(), 0.0)); } + for (auto ii : rec->hops ()) { ii->setInventoryAmount(std::max( ii->inventory() - ii->amount_kg(), 0.0)); } + for (auto ii : rec->yeasts ()) { ii->setInventoryQuanta(std::max(static_cast(ii->inventory()) - 1, 0 )); } } + + return; } // Need to make sure the recipe tree is active, I think @@ -2814,10 +2787,8 @@ void MainWindow::closeEvent(QCloseEvent* /*event*/) } -void MainWindow::copyRecipe() -{ +void MainWindow::copyRecipe() { QString name = QInputDialog::getText( this, tr("Copy Recipe"), tr("Enter a unique name for the copy.") ); - if (name.isEmpty()) { return; } @@ -3206,19 +3177,21 @@ void MainWindow::versionedRecipe(Recipe* descendant) treeView_recipe->setCurrentIndex(ndx); } -void MainWindow::closeBrewNote(int brewNoteId, std::shared_ptr object) { +// .:TBD:. Seems redundant to pass both the brewnote ID and a pointer to it; we only need one of these +void MainWindow::closeBrewNote([[maybe_unused]] int brewNoteId, std::shared_ptr object) { BrewNote* b = std::static_pointer_cast(object).get(); Recipe* parent = ObjectStoreWrapper::getByIdRaw(b->getRecipeId()); // If this isn't the focused recipe, do nothing because there are no tabs // to close. - if ( parent != recipeObs ) + if (parent != recipeObs) { return; + } BrewNoteWidget* ni = findBrewNoteWidget(b); - - if ( ni ) + if (ni) { tabWidget_recipeView->removeTab( tabWidget_recipeView->indexOf(ni)); + } return; diff --git a/src/MashDesigner.cpp b/src/MashDesigner.cpp index b52b6986f..b93c18728 100644 --- a/src/MashDesigner.cpp +++ b/src/MashDesigner.cpp @@ -72,9 +72,9 @@ MashDesigner::MashDesigner(QWidget * parent) : QDialog {parent}, return; } -void MashDesigner::proceed() -{ +void MashDesigner::proceed() { nextStep(++curStep); + return; } void MashDesigner::setRecipe(Recipe* rec) { @@ -154,8 +154,8 @@ void MashDesigner::saveStep() { this->mashStep->setName(this->lineEdit_name->text()); this->mashStep->setType(static_cast(comboBox_type->currentIndex())); // Bound the target temperature to what can be achieved - this->mashStep->setStepTemp_c(this->bound_temp_c(this->lineEdit_temp->toSI().quantity)); - this->mashStep->setStepTime_min(lineEdit_time->toSI().quantity); + this->mashStep->setStepTemp_c(this->bound_temp_c(this->lineEdit_temp->toCanonical().quantity())); + this->mashStep->setStepTime_min(lineEdit_time->toCanonical().quantity()); // finish a few things -- this may be premature optimization if (isInfusion()) { @@ -169,7 +169,7 @@ void MashDesigner::saveStep() { } double MashDesigner::stepTemp_c() { - return lineEdit_temp->toSI().quantity; + return lineEdit_temp->toCanonical().quantity(); } bool MashDesigner::heating() { @@ -351,7 +351,7 @@ bool MashDesigner::initializeMash() { // Order matters. Don't do this until every that could return false has this->mash->setTunSpecificHeat_calGC(this->equip->tunSpecificHeat_calGC()); this->mash->setTunWeight_kg(this->equip->tunWeight_kg()); - this->mash->setTunTemp_c(Measurement::qStringToSI(dialogText, Measurement::PhysicalQuantity::Temperature).quantity); + this->mash->setTunTemp_c(Measurement::qStringToSI(dialogText, Measurement::PhysicalQuantity::Temperature).quantity()); this->curStep = 0; this->addedWater_l = 0; @@ -646,12 +646,12 @@ double MashDesigner::getDecoctionAmount_l() { bool MashDesigner::isBatchSparge() const { MashStep::Type stepType = type(); - return (stepType == MashStep::batchSparge); + return (stepType == MashStep::Type::batchSparge); } bool MashDesigner::isFlySparge() const { MashStep::Type stepType = type(); - return (stepType == MashStep::flySparge); + return (stepType == MashStep::Type::flySparge); } bool MashDesigner::isSparge() const { @@ -660,17 +660,17 @@ bool MashDesigner::isSparge() const { bool MashDesigner::isInfusion() const { MashStep::Type stepType = type(); - return (stepType == MashStep::Infusion || isSparge()); + return (stepType == MashStep::Type::Infusion || isSparge()); } bool MashDesigner::isDecoction() const { MashStep::Type stepType = type(); - return (stepType == MashStep::Decoction); + return (stepType == MashStep::Type::Decoction); } bool MashDesigner::isTemperature() const { MashStep::Type stepType = type(); - return (stepType == MashStep::Temperature); + return (stepType == MashStep::Type::Temperature); } MashStep::Type MashDesigner::type() const { diff --git a/src/MashEditor.cpp b/src/MashEditor.cpp index 971c29ba2..2116e2862 100644 --- a/src/MashEditor.cpp +++ b/src/MashEditor.cpp @@ -62,12 +62,12 @@ void MashEditor::saveAndClose() { 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->toSI().quantity); - mashObs->setSpargeTemp_c(lineEdit_spargeTemp->toSI().quantity); - mashObs->setPh(lineEdit_spargePh->toSI().quantity); - mashObs->setTunTemp_c(lineEdit_tunTemp->toSI().quantity); - mashObs->setTunWeight_kg(lineEdit_tunMass->toSI().quantity); - mashObs->setTunSpecificHeat_calGC(lineEdit_tunSpHeat->toSI().quantity); + 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()); diff --git a/src/MashStepEditor.cpp b/src/MashStepEditor.cpp index d48a807ab..37b55667e 100644 --- a/src/MashStepEditor.cpp +++ b/src/MashStepEditor.cpp @@ -54,7 +54,7 @@ void MashStepEditor::showChanges(QMetaProperty* metaProp) { if (updateAll) { lineEdit_name->setText(obs->name()); - comboBox_type->setCurrentIndex(obs->type()); + 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()); @@ -65,7 +65,7 @@ void MashStepEditor::showChanges(QMetaProperty* metaProp) { } else if (propName == PropertyNames::NamedEntity::name) { lineEdit_name->setText(obs->name()); } else if (propName == PropertyNames::MashStep::type) { - comboBox_type->setCurrentIndex(obs->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) { @@ -128,13 +128,13 @@ 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->toSI().quantity); - obs->setInfuseTemp_c(lineEdit_infuseTemp->toSI().quantity); - obs->setDecoctionAmount_l(lineEdit_decoctionAmount->toSI().quantity); - obs->setStepTemp_c(lineEdit_stepTemp->toSI().quantity); - obs->setStepTime_min(lineEdit_stepTime->toSI().quantity); - obs->setRampTime_min(lineEdit_rampTime->toSI().quantity); - obs->setEndTemp_c(lineEdit_endTemp->toSI().quantity); + 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()); if (this->obs->key() < 0) { // This is a new MashStep, so we need to store it. diff --git a/src/MashWizard.cpp b/src/MashWizard.cpp index 410fa0dd9..451a9462b 100644 --- a/src/MashWizard.cpp +++ b/src/MashWizard.cpp @@ -93,7 +93,7 @@ void MashWizard::show() // Recalculate the mash thickness double thickNum = firstStep->infuseAmount_l()/recObs->grainsInMash_kg(); - double thickness = thickNum * weightUnit->toSI(1).quantity / volumeUnit->toSI(1).quantity ; + double thickness = thickNum * weightUnit->toCanonical(1).quantity() / volumeUnit->toCanonical(1).quantity() ; doubleSpinBox_thickness->setValue(thickness); // Is this a batch, fly or no sparge? @@ -101,7 +101,7 @@ void MashWizard::show() radioButton_noSparge->setChecked(true); widget_batches->setEnabled(false); widget_mashThickness->setEnabled(false); - } else if ( lastStep->type() == MashStep::flySparge ) { + } else if ( lastStep->type() == MashStep::Type::flySparge ) { radioButton_flySparge->setChecked(true); widget_batches->setEnabled(false); widget_mashThickness->setEnabled(true); @@ -202,7 +202,7 @@ void MashWizard::wizardry() { grainMass = recObs->grainsInMash_kg(); if ( bGroup->checkedButton() != radioButton_noSparge ) { thickNum = doubleSpinBox_thickness->value(); - thickness_LKg = thickNum * volumeUnit->toSI(1).quantity / weightUnit->toSI(1).quantity; + thickness_LKg = thickNum * volumeUnit->toCanonical(1).quantity() / weightUnit->toCanonical(1).quantity(); } else { // not sure I like this. Why is this here and not somewhere later? if (steps.size() == 1 ) { @@ -362,7 +362,7 @@ void MashWizard::wizardry() { double volPerBatch = spargeWater_l/numSteps; // its evil, but deal with it for(int i=0; i < numSteps; ++i ) { auto newMashStep = std::make_shared(tr("Batch Sparge %1").arg(i+1)); - newMashStep->setType(MashStep::batchSparge); + newMashStep->setType(MashStep::Type::batchSparge); newMashStep->setInfuseAmount_l(volPerBatch); newMashStep->setInfuseTemp_c(tw); newMashStep->setEndTemp_c(tw); @@ -383,7 +383,7 @@ void MashWizard::wizardry() { // fly sparge, I think else { auto newMashStep = std::make_shared(tr("Fly Sparge")); - newMashStep->setType(MashStep::flySparge); + newMashStep->setType(MashStep::Type::flySparge); newMashStep->setInfuseAmount_l(spargeWater_l); newMashStep->setInfuseTemp_c(tw); newMashStep->setEndTemp_c(tw); diff --git a/src/MiscEditor.cpp b/src/MiscEditor.cpp index 81f5d1cf5..4fc836f18 100644 --- a/src/MiscEditor.cpp +++ b/src/MiscEditor.cpp @@ -55,6 +55,7 @@ void MiscEditor::setMisc(Misc * m) { connect(obsMisc, SIGNAL(changed(QMetaProperty, QVariant)), this, SLOT(changed(QMetaProperty, QVariant))); showChanges(); } + return; } void MiscEditor::save() { @@ -69,7 +70,7 @@ void MiscEditor::save() { this->obsMisc->setName(lineEdit_name->text()); this->obsMisc->setType(static_cast(comboBox_type->currentIndex())); this->obsMisc->setUse(static_cast(comboBox_use->currentIndex())); - this->obsMisc->setTime(lineEdit_time->toSI().quantity); + this->obsMisc->setTime(lineEdit_time->toCanonical().quantity()); this->obsMisc->setAmountIsWeight((checkBox_isWeight->checkState() == Qt::Checked) ? true : false); this->obsMisc->setUseFor(textEdit_useFor->toPlainText()); this->obsMisc->setNotes(textEdit_notes->toPlainText()); @@ -79,7 +80,7 @@ void MiscEditor::save() { ObjectStoreWrapper::insert(*this->obsMisc); } // do this late to make sure we've the row in the inventory table - this->obsMisc->setInventoryAmount(lineEdit_inventory->toSI().quantity); + this->obsMisc->setInventoryAmount(lineEdit_inventory->toCanonical().quantity()); setVisible(false); return; } diff --git a/src/MiscSortFilterProxyModel.cpp b/src/MiscSortFilterProxyModel.cpp index b019f9e85..9b55f644a 100644 --- a/src/MiscSortFilterProxyModel.cpp +++ b/src/MiscSortFilterProxyModel.cpp @@ -44,7 +44,7 @@ bool MiscSortFilterProxyModel::lessThan(const QModelIndex &left, switch (left.column()) { case MISCINVENTORYCOL: - if (Measurement::qStringToSI(leftMisc.toString(), Measurement::PhysicalQuantity::Mass).quantity == 0.0 && + if (Measurement::qStringToSI(leftMisc.toString(), Measurement::PhysicalQuantity::Mass).quantity() == 0.0 && this->sortOrder() == Qt::AscendingOrder) { return false; } diff --git a/src/NamedMashEditor.cpp b/src/NamedMashEditor.cpp index 0506a1e4a..e06a5f01a 100644 --- a/src/NamedMashEditor.cpp +++ b/src/NamedMashEditor.cpp @@ -86,6 +86,7 @@ void NamedMashEditor::showEditor() { void NamedMashEditor::closeEditor() { this->setVisible(false); + return; } void NamedMashEditor::saveAndClose() { @@ -95,41 +96,41 @@ void NamedMashEditor::saveAndClose() { qDebug() << Q_FUNC_INFO << "Saving mash (#" << this->mashObs->key() << ")"; - // using toSI aon the spargePh is something of a cheat, but the btLineEdit + // using toCanonical aon the spargePh is something of a cheat, but the btLineEdit // class will do the right thing. That is how a plan comes together. 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(lineEdit_name->text()); - this->mashObs->setGrainTemp_c(lineEdit_grainTemp->toSI().quantity); - this->mashObs->setSpargeTemp_c(lineEdit_spargeTemp->toSI().quantity); - this->mashObs->setPh(lineEdit_spargePh->toSI().quantity); - this->mashObs->setTunTemp_c(lineEdit_tunTemp->toSI().quantity); - this->mashObs->setTunWeight_kg(lineEdit_tunMass->toSI().quantity); - this->mashObs->setTunSpecificHeat_calGC(lineEdit_tunSpHeat->toSI().quantity); + this->mashObs->setGrainTemp_c(lineEdit_grainTemp->toCanonical().quantity()); + this->mashObs->setSpargeTemp_c(lineEdit_spargeTemp->toCanonical().quantity()); + this->mashObs->setPh(lineEdit_spargePh->toCanonical().quantity()); + this->mashObs->setTunTemp_c(lineEdit_tunTemp->toCanonical().quantity()); + this->mashObs->setTunWeight_kg(lineEdit_tunMass->toCanonical().quantity()); + this->mashObs->setTunSpecificHeat_calGC(lineEdit_tunSpHeat->toCanonical().quantity()); this->mashObs->setNotes( textEdit_notes->toPlainText() ); return; } -void NamedMashEditor::setMash(Mash* mash) -{ - if( mashObs ) +void NamedMashEditor::setMash(Mash* mash) { + if( mashObs ) { disconnect( mashObs, 0, this, 0 ); + } mashObs = mash; mashStepTableModel->setMash(mashObs); - if( mashObs ) - { + if (mashObs) { connect( mashObs, &NamedEntity::changed, this, &NamedMashEditor::changed ); showChanges(); } } -void NamedMashEditor::changed(QMetaProperty prop, QVariant /*val*/) -{ - if( sender() == mashObs ) +void NamedMashEditor::changed(QMetaProperty prop, QVariant /*val*/) { + if (sender() == mashObs) { showChanges(&prop); + } + return; } void NamedMashEditor::showChanges(QMetaProperty* prop) { @@ -190,8 +191,7 @@ void NamedMashEditor::showChanges(QMetaProperty* prop) { } } -void NamedMashEditor::clear() -{ +void NamedMashEditor::clear() { lineEdit_name->setText(QString("")); lineEdit_grainTemp->setText(QString("")); lineEdit_spargeTemp->setText(QString("")); @@ -201,7 +201,7 @@ void NamedMashEditor::clear() lineEdit_tunSpHeat->setText(QString("")); textEdit_notes->setPlainText(""); - + return; } void NamedMashEditor::addMashStep() { @@ -217,30 +217,30 @@ void NamedMashEditor::addMashStep() { return; } -bool NamedMashEditor::justOne(QModelIndexList selected) -{ - int row, size, i; - - size = selected.size(); - if ( ! size ) +bool NamedMashEditor::justOne(QModelIndexList selected) { + int size = selected.size(); + if ( ! size ) { return false; + } - row = selected[0].row(); - for( i = 1; i < size; ++i ) - { - if ( selected[i].row() != row ) + int row = selected[0].row(); + for (int ii = 1; ii < size; ++ii) { + if (selected[ii].row() != row) { return false; + } } return true; } void NamedMashEditor::removeMashStep() { - if ( ! mashObs ) + if ( ! mashObs ) { return; + } QModelIndexList selected = mashStepTableWidget->selectionModel()->selectedIndexes(); - if ( !justOne(selected) ) + if ( !justOne(selected) ) { return; + } auto step = mashStepTableModel->getRow(selected[0].row()); this->mashObs->removeMashStep(step); @@ -281,15 +281,13 @@ void NamedMashEditor::moveMashStepDown() return; } -void NamedMashEditor::mashSelected(const QString& name) -{ +void NamedMashEditor::mashSelected([[maybe_unused]] QString const & name) { Mash* selected = mashListModel->at(mashComboBox->currentIndex()); if (selected && selected != mashObs) setMash(selected); } -void NamedMashEditor::fromEquipment(const QString& name) -{ +void NamedMashEditor::fromEquipment([[maybe_unused]] QString const & name) { if( mashObs == 0 ) return; Equipment* selected = equipListModel->at(equipmentComboBox->currentIndex()); diff --git a/src/OgAdjuster.cpp b/src/OgAdjuster.cpp index 4d17429ed..304932a6c 100644 --- a/src/OgAdjuster.cpp +++ b/src/OgAdjuster.cpp @@ -34,65 +34,47 @@ OgAdjuster::OgAdjuster( QWidget* parent ) : QDialog(parent) { } -void OgAdjuster::setRecipe( Recipe* rec ) -{ - if( rec && rec != recObs ) - { +void OgAdjuster::setRecipe(Recipe* rec) { + if (rec && rec != recObs) { recObs = rec; } + return; } // TODO: There are a LOT of assumptions and simplifications here. Need to change that. -void OgAdjuster::calculate() -{ - Equipment* equip; - double sg = 0.0; - double temp_c = 0.0; - double plato = 0.0; - double wort_l = 0.0; - double hydroTemp_c = 0.0; - - double sugar_kg = 0.0; - double water_kg = 0.0; - double sg_15C = 0.0; - double sg_20C = 0.0; - double evapRate_lHr = 0.0; - - double finalPlato = 0.0; - double finalVolume_l = 0.0; - double finalWater_kg = 0.0; - double finalUncorrectedSg_20C = 0.0; - double waterToAdd_kg = 0.0; - double waterToAdd_l = 0.0; - - bool gotSG = false; - bool okPlato = true; +void OgAdjuster::calculate() { // Get inputs. - sg = lineEdit_sg->toSI().quantity; - plato = lineEdit_plato->toDoubleRaw(&okPlato); - temp_c = lineEdit_temp->toSI().quantity; - hydroTemp_c = lineEdit_calTemp->toSI().quantity; - wort_l = lineEdit_volume->toSI().quantity; + double sg = lineEdit_sg->toCanonical().quantity(); + bool okPlato = true; + double plato = lineEdit_plato->toDoubleRaw(&okPlato); + double temp_c = lineEdit_temp->toCanonical().quantity(); + double hydroTemp_c = lineEdit_calTemp->toCanonical().quantity(); + double wort_l = lineEdit_volume->toCanonical().quantity(); // Make sure we got enough info. - gotSG = sg != 0 && temp_c != 0 && hydroTemp_c != 0; + bool gotSG = sg != 0 && temp_c != 0 && hydroTemp_c != 0; - if( wort_l == 0 ) + if (wort_l == 0) { return; - if( ! gotSG && ! okPlato ) + } + + if (!gotSG && !okPlato) { return; + } - if( recObs == 0 || recObs->equipment() == 0 ) + if (recObs == 0 || recObs->equipment() == 0) { return; + } - equip = recObs->equipment(); - evapRate_lHr = equip->evapRate_lHr(); + Equipment* equip = recObs->equipment(); + double evapRate_lHr = equip->evapRate_lHr(); // Calculate missing input parameters. + double sg_20C = 0.0; if( gotSG ) { - sg_15C = sg * Algorithms::getWaterDensity_kgL(hydroTemp_c)/Algorithms::getWaterDensity_kgL(15) + Algorithms::hydrometer15CCorrection( temp_c ); + 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 ); @@ -104,29 +86,29 @@ void OgAdjuster::calculate() } // Calculate intermediate parameters. - sugar_kg = sg_20C * Algorithms::getWaterDensity_kgL(20) * wort_l * plato/(double)100; + double sugar_kg = sg_20C * Algorithms::getWaterDensity_kgL(20) * wort_l * plato/(double)100; //std::cerr << "sugar_kg = " << sugar_kg << std::endl; - water_kg = sg_20C * Algorithms::getWaterDensity_kgL(20) * wort_l * ((double)1 - plato/(double)100); + double water_kg = sg_20C * Algorithms::getWaterDensity_kgL(20) * wort_l * ((double)1 - plato/(double)100); //std::cerr << "water_kg = " << water_kg << std::endl; // Calculate OG w/o correction. - finalVolume_l = equip->wortEndOfBoil_l(wort_l); - finalWater_kg = water_kg - equip->boilTime_min()/(double)60 * evapRate_lHr * Algorithms::getWaterDensity_kgL(20); + double finalVolume_l = equip->wortEndOfBoil_l(wort_l); + double finalWater_kg = water_kg - equip->boilTime_min()/(double)60 * evapRate_lHr * Algorithms::getWaterDensity_kgL(20); //std::cerr << "finalWater_kg = " << finalWater_kg << std::endl; //std::cerr << "boilTime = " << equip->getBoilTime_min() << std::endl; //std::cerr << "evapRate_lHr = " << evapRate_lHr << std::endl; //std::cerr << "waterDensity = " << Algorithms::getWaterDensity_kgL(20) << std::endl; - finalPlato = (double)100 * sugar_kg / (sugar_kg + finalWater_kg); + double finalPlato = (double)100 * sugar_kg / (sugar_kg + finalWater_kg); //std::cerr << "finalPlato = " << finalPlato << std::endl; - finalUncorrectedSg_20C = Algorithms::PlatoToSG_20C20C( finalPlato ); + double finalUncorrectedSg_20C = Algorithms::PlatoToSG_20C20C( finalPlato ); // Calculate volume to add to boil finalPlato = Algorithms::SG_20C20C_toPlato( recObs->og() ); // This is bad. This assumes the post-boil gravity = og. Need account for post-boil water additions. // postBoilWater_kg = batchSize - topUpWater; // postBoilSugar_kg = Algorithms::SG_20C20C_toPlato( recObs->getOG() ) / 100.0 * batchSize * recObs->getOG() * Algorithms::getWaterDensity_kgL(20); // finalPlato = 100 * postBoilSugar_kg / ( postBoilSugar_kg + postBoilWater_kg ); - waterToAdd_kg = (double)100 * sugar_kg / finalPlato - sugar_kg - finalWater_kg; - waterToAdd_l = waterToAdd_kg / Algorithms::getWaterDensity_kgL(20); + double waterToAdd_kg = (double)100 * sugar_kg / finalPlato - sugar_kg - finalWater_kg; + double waterToAdd_l = waterToAdd_kg / Algorithms::getWaterDensity_kgL(20); // Calculate final batch size. finalVolume_l += waterToAdd_l; @@ -135,4 +117,5 @@ void OgAdjuster::calculate() lineEdit_og->setText(finalUncorrectedSg_20C); lineEdit_add->setText(waterToAdd_l); lineEdit_batchSize->setText(finalVolume_l); + return; } diff --git a/src/PersistentSettings.h b/src/PersistentSettings.h index dc1dc5545..c4b296ae7 100644 --- a/src/PersistentSettings.h +++ b/src/PersistentSettings.h @@ -61,6 +61,7 @@ AddSettingName(deletewhat) AddSettingName(directory) // backups section AddSettingName(files) // backups section AddSettingName(firstWortHopAdjustment) +AddSettingName(forcedLocale) AddSettingName(frequency) // backups section AddSettingName(geometry) AddSettingName(ibu_formula) diff --git a/src/PitchDialog.cpp b/src/PitchDialog.cpp index 7ef8b4042..118cbe098 100644 --- a/src/PitchDialog.cpp +++ b/src/PitchDialog.cpp @@ -68,25 +68,22 @@ void PitchDialog::updateProductionDate() { this->dateEdit_ProductionDate->setDisplayFormat(format); } -void PitchDialog::setWortVolume_l(double volume) -{ +void PitchDialog::setWortVolume_l(double volume) { lineEdit_vol->setText(volume); } -void PitchDialog::setWortDensity(double sg) -{ +void PitchDialog::setWortDensity(double sg) { lineEdit_OG->setText(sg); } -void PitchDialog::calculate() -{ +void PitchDialog::calculate() { // Allow selection of 0.75 to 2 million cells per mL per degree P. double rate_MpermLP = (2-0.75) * ((double)slider_pitchRate->value()) / 100.0 + 0.75; // This isn't right. - double og = lineEdit_OG->toSI().quantity; - double vol_l = lineEdit_vol->toSI().quantity; + double og = lineEdit_OG->toCanonical().quantity(); + double vol_l = lineEdit_vol->toCanonical().quantity(); // I somewhat aribtrarily defined "SI" for density to be specific gravity. // Since these calcs need plato, convert @@ -101,8 +98,7 @@ void PitchDialog::calculate() // Set the aeration factor for the starter size double aerationFactor; - switch (comboBox_AerationMethod->currentIndex()) - { + switch (comboBox_AerationMethod->currentIndex()) { case 1: // O2 at the start aerationFactor = 1.33; break; @@ -125,47 +121,45 @@ void PitchDialog::calculate() lineEdit_starterVol->setText(starterVol_l); lineEdit_yeast->setText(dry_g/1000); //Needs to be converted into default unit (kg) lineEdit_vials->setText(vials,0); + return; } -void PitchDialog::updateShownPitchRate(int percent) -{ +void PitchDialog::updateShownPitchRate(int percent) { // Allow selection of 0.75 to 2 million cells per mL per degree P. 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')) ); + return; } /* * Toggles whether or not the viability box and date edit * is enabled or disabled. */ -void PitchDialog::toggleViabilityFromDate(int state) -{ - if (state == Qt::Unchecked) - { +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); - } - else if (state == Qt::Checked) - { + } 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()); } + return; } /* * Updates the current viability based on the date. */ -void PitchDialog::updateViabilityFromDate(QDate date) -{ +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); + return; } diff --git a/src/PrimingDialog.cpp b/src/PrimingDialog.cpp index 479cdb5ce..b1e476f6c 100644 --- a/src/PrimingDialog.cpp +++ b/src/PrimingDialog.cpp @@ -39,58 +39,40 @@ PrimingDialog::PrimingDialog(QWidget* parent) : QDialog(parent) { PrimingDialog::~PrimingDialog() = default; -void PrimingDialog::calculate() -{ - QAbstractButton* button; +void PrimingDialog::calculate() { - double beer_l; - double temp_c; - double desiredVols; + double beer_l = lineEdit_beerVol->toCanonical().quantity(); + double temp_c = lineEdit_temp->toCanonical().quantity(); + double desiredVols = lineEdit_vols->toCanonical().quantity(); - double addedVols; - double residualVols; - - double co2_l; - double co2_mol; + 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 sugar_mol; double sugar_g; - beer_l = lineEdit_beerVol->toSI().quantity; - temp_c = lineEdit_temp->toSI().quantity; - desiredVols = lineEdit_vols->toSI().quantity; - - residualVols = 1.57 * pow( 0.97, temp_c ); // Amount of CO2 still in suspension. - addedVols = desiredVols - residualVols; - co2_l = addedVols * beer_l; // Liters of CO2 we need to generate (at 273 K and 1 atm). - co2_mol = co2_l / 22.4; // Mols of CO2 we need. - - button = sugarGroup->checkedButton(); - - if( button == radioButton_glucMono ) - { + QAbstractButton* button = sugarGroup->checkedButton(); + if (button == radioButton_glucMono) { sugar_mol = co2_mol / 2; sugar_g = sugar_mol * 198; // Glucose monohydrate is 198 g/mol. - } - else if( button == radioButton_gluc ) - { + } else if (button == radioButton_gluc) { sugar_mol = co2_mol / 2; sugar_g = sugar_mol * 180; // Glucose is 180g/mol. - } - else if( button == radioButton_sucrose ) - { + } else if (button == radioButton_sucrose) { sugar_mol = co2_mol / 4; sugar_g = sugar_mol * 342; // Sucrose is 342 g/mol. - } - else if( button == radioButton_dme ) - { + } else if (button == radioButton_dme) { sugar_mol = co2_mol / 2; sugar_g = sugar_mol * 180 / 0.60; // DME is equivalently about 60% glucose. - } - else + } else { sugar_g = 0; + } //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 ); + + return; } diff --git a/src/RecipeExtrasWidget.cpp b/src/RecipeExtrasWidget.cpp index 62d689dee..d9a711d21 100644 --- a/src/RecipeExtrasWidget.cpp +++ b/src/RecipeExtrasWidget.cpp @@ -90,7 +90,7 @@ void RecipeExtrasWidget::updateBrewerAsst() return; } -void RecipeExtrasWidget::changeRatings(int rating) { ratingChanged = true; } +void RecipeExtrasWidget::changeRatings([[maybe_unused]] int rating) { ratingChanged = true; } void RecipeExtrasWidget::updateTasteRating() { @@ -109,7 +109,7 @@ void RecipeExtrasWidget::updatePrimaryAge() if( recipe == 0 ) return; - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::primaryAge_days, lineEdit_primaryAge->toSI().quantity, tr("Change Primary Age")); + MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::primaryAge_days, lineEdit_primaryAge->toCanonical().quantity(), tr("Change Primary Age")); } void RecipeExtrasWidget::updatePrimaryTemp() @@ -117,7 +117,7 @@ void RecipeExtrasWidget::updatePrimaryTemp() if( recipe == 0 ) return; - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::primaryTemp_c, lineEdit_primaryTemp->toSI().quantity, tr("Change Primary Temp")); + MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::primaryTemp_c, lineEdit_primaryTemp->toCanonical().quantity(), tr("Change Primary Temp")); } void RecipeExtrasWidget::updateSecondaryAge() @@ -125,7 +125,7 @@ void RecipeExtrasWidget::updateSecondaryAge() if( recipe == 0 ) return; - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::secondaryAge_days, lineEdit_secAge->toSI().quantity, tr("Change Secondary Age")); + MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::secondaryAge_days, lineEdit_secAge->toCanonical().quantity(), tr("Change Secondary Age")); } void RecipeExtrasWidget::updateSecondaryTemp() @@ -133,7 +133,7 @@ void RecipeExtrasWidget::updateSecondaryTemp() if( recipe == 0 ) return; - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::secondaryTemp_c, lineEdit_secTemp->toSI().quantity, tr("Change Secondary Temp")); + MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::secondaryTemp_c, lineEdit_secTemp->toCanonical().quantity(), tr("Change Secondary Temp")); } void RecipeExtrasWidget::updateTertiaryAge() @@ -141,7 +141,7 @@ void RecipeExtrasWidget::updateTertiaryAge() if( recipe == 0 ) return; - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::tertiaryAge_days, lineEdit_tertAge->toSI().quantity, tr("Change Tertiary Age")); + MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::tertiaryAge_days, lineEdit_tertAge->toCanonical().quantity(), tr("Change Tertiary Age")); } void RecipeExtrasWidget::updateTertiaryTemp() @@ -149,7 +149,7 @@ void RecipeExtrasWidget::updateTertiaryTemp() if( recipe == 0 ) return; - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::tertiaryTemp_c, lineEdit_tertTemp->toSI().quantity, tr("Change Tertiary Temp")); + MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::tertiaryTemp_c, lineEdit_tertTemp->toCanonical().quantity(), tr("Change Tertiary Temp")); } void RecipeExtrasWidget::updateAge() @@ -157,7 +157,7 @@ void RecipeExtrasWidget::updateAge() if( recipe == 0 ) return; - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::age, lineEdit_age->toSI().quantity, tr("Change Age")); + MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::age, lineEdit_age->toCanonical().quantity(), tr("Change Age")); } void RecipeExtrasWidget::updateAgeTemp() @@ -165,7 +165,7 @@ void RecipeExtrasWidget::updateAgeTemp() if( recipe == 0 ) return; - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::ageTemp_c, lineEdit_ageTemp->toSI().quantity, tr("Change Age Temp")); + MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::ageTemp_c, lineEdit_ageTemp->toCanonical().quantity(), tr("Change Age Temp")); } void RecipeExtrasWidget::updateDate(QDate const & date) { @@ -193,7 +193,7 @@ void RecipeExtrasWidget::updateCarbonation() MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::carbonation_vols, - lineEdit_carbVols->toSI().quantity, + lineEdit_carbVols->toCanonical().quantity(), tr("Change Carbonation")); } diff --git a/src/StrikeWaterDialog.cpp b/src/StrikeWaterDialog.cpp index a85d84dce..aaeae45d2 100644 --- a/src/StrikeWaterDialog.cpp +++ b/src/StrikeWaterDialog.cpp @@ -23,61 +23,74 @@ #include -// From Northern Brewer ~0.38 but Jon Palmer suggest 0.41 -// to compensate for the lost to the tun even if the tun is pre-heaten -const double StrikeWaterDialog::specificHeatBarley = 0.41; +namespace { + // From Northern Brewer ~0.38 but Jon Palmer suggest 0.41 + // to compensate for the lost to the tun even if the tun is pre-heated + double const specificHeatBarley = 0.41; -StrikeWaterDialog::StrikeWaterDialog(QWidget* parent) : QDialog(parent) -{ + /** + * \brief + */ + double initialInfusionSi(double grainTemp, double targetTemp, double waterToGrain) { + if (waterToGrain == 0.0) { + return 0.0; + } + return (specificHeatBarley / waterToGrain) * (targetTemp - grainTemp) + targetTemp; + } + + /** + * \brief + */ + double mashInfusionSi(double initialTemp, + double targetTemp, + double grainWeight, + double infusionWater, + double mashVolume) { + if (infusionWater - targetTemp == 0.0) { + return 0.0; + } + + return ((targetTemp - initialTemp) * (specificHeatBarley * grainWeight + mashVolume)) / (infusionWater - targetTemp); + } + +} + +StrikeWaterDialog::StrikeWaterDialog(QWidget* parent) : QDialog(parent) { setupUi(this); connect(pushButton_calculate, &QAbstractButton::clicked, this, &StrikeWaterDialog::calculate); + return; } -StrikeWaterDialog::~StrikeWaterDialog() {} +StrikeWaterDialog::~StrikeWaterDialog() = default; -void StrikeWaterDialog::calculate() -{ +void StrikeWaterDialog::calculate() { double initial = computeInitialInfusion(); double mash = computeMashInfusion(); - initialResultTxt->setText(initial); - mashResultTxt->setText(mash); + this->initialResultTxt->setText(initial); + this->mashResultTxt->setText(mash); + return; } -double StrikeWaterDialog::computeInitialInfusion() -{ - double grainTemp = grainTempVal->toSI().quantity; - double targetMash = targetMashVal->toSI().quantity; - double waterVolume = waterVolumeVal->toSI().quantity; - double grainWeight = grainWeightInitVal->toSI().quantity; - - if ( grainWeight == 0.0 ) - return 0.0; +double StrikeWaterDialog::computeInitialInfusion() { + double grainTemp = this->grainTempVal->toCanonical().quantity(); + double targetMash = this->targetMashVal->toCanonical().quantity(); + double waterVolume = this->waterVolumeVal->toCanonical().quantity(); + double grainWeight = this->grainWeightInitVal->toCanonical().quantity(); - return initialInfusionSi( grainTemp, targetMash, waterVolume / grainWeight); -} - -double StrikeWaterDialog::computeMashInfusion() -{ - double mashVol = mashVolVal->toSI().quantity; - double grainWeight = grainWeightVal->toSI().quantity; - double actualMash = actualMashVal->toSI().quantity; - double targetMashInf = targetMashInfVal->toSI().quantity; - double infusionWater = infusionWaterVal->toSI().quantity; + if (grainWeight == 0.0) { + return 0.0; + } - return mashInfusionSi(actualMash, targetMashInf, grainWeight, infusionWater, mashVol); + return initialInfusionSi(grainTemp, targetMash, waterVolume / grainWeight); } -double StrikeWaterDialog::initialInfusionSi(double grainTemp, double targetTemp, double waterToGrain) -{ - if ( waterToGrain == 0.0 ) - return 0.0; - return (specificHeatBarley / waterToGrain) * (targetTemp - grainTemp) + targetTemp; -} -double StrikeWaterDialog::mashInfusionSi(double initialTemp, double targetTemp, double grainWeight, double infusionWater, double mashVolume) -{ - if ( infusionWater - targetTemp == 0.0 ) - return 0.0; +double StrikeWaterDialog::computeMashInfusion() { + 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(); - return ((targetTemp - initialTemp) * (specificHeatBarley * grainWeight + mashVolume)) / (infusionWater - targetTemp); + return mashInfusionSi(actualMash, targetMashInf, grainWeight, infusionWater, mashVol); } diff --git a/src/StrikeWaterDialog.h b/src/StrikeWaterDialog.h index 34461ea17..309cb28ad 100644 --- a/src/StrikeWaterDialog.h +++ b/src/StrikeWaterDialog.h @@ -35,25 +35,25 @@ * * \brief Dialog to calculate the amount and temperature of the strike water. */ -class StrikeWaterDialog : public QDialog, public Ui::strikeWaterDialog -{ - Q_OBJECT - public: - StrikeWaterDialog(QWidget* parent = 0); - ~StrikeWaterDialog(); - - public slots: - void calculate(); - - private: - static const double specificHeatBarley; - - double initialInfusionSi(double grainTemp, double targetTemp, double waterToGrain); - double mashInfusionSi(double initialTemp, double targetTemp, - double grainWeight, double infusionWater, double mashVolume); - - double computeInitialInfusion(); - double computeMashInfusion(); +class StrikeWaterDialog : public QDialog, public Ui::strikeWaterDialog { + Q_OBJECT +public: + StrikeWaterDialog(QWidget* parent = 0); + ~StrikeWaterDialog(); + +public slots: + void calculate(); + +private: + /** + * \brief + */ + double computeInitialInfusion(); + + /** + * \brief + */ + double computeMashInfusion(); }; #endif diff --git a/src/StyleEditor.cpp b/src/StyleEditor.cpp index 7ebf0cc3b..e322e2b88 100644 --- a/src/StyleEditor.cpp +++ b/src/StyleEditor.cpp @@ -29,15 +29,14 @@ #include "StyleListModel.h" #include "StyleSortFilterProxyModel.h" -StyleEditor::StyleEditor(QWidget* parent, bool singleStyleEditor) : QDialog(parent), obsStyle(nullptr) { +StyleEditor::StyleEditor(QWidget* parent, bool singleStyleEditor) : QDialog{parent}, obsStyle{nullptr} { setupUi(this); - if ( singleStyleEditor ) - { - for(int i = 0; i < horizontalLayout_styles->count(); ++i) - { + if (singleStyleEditor) { + for (int i = 0; i < horizontalLayout_styles->count(); ++i) { QWidget* w = horizontalLayout_styles->itemAt(i)->widget(); - if(w) + if (w) { w->setVisible(false); + } } pushButton_new->setVisible(false); @@ -58,21 +57,22 @@ StyleEditor::StyleEditor(QWidget* parent, bool singleStyleEditor) : QDialog(pare connect( styleComboBox, SIGNAL(activated( const QString& )), this, SLOT( styleSelected(const QString&) ) ); setStyle( styleListModel->at(styleComboBox->currentIndex())); + return; } -void StyleEditor::setStyle( Style* s ) -{ - if( obsStyle ) +void StyleEditor::setStyle( Style* s ) { + if (obsStyle) { disconnect( obsStyle, 0, this, 0 ); + } obsStyle = s; - if( obsStyle ) - { + if (obsStyle) { connect( obsStyle, &NamedEntity::changed, this, &StyleEditor::changed ); showChanges(); } styleComboBox->setCurrentIndex(styleListModel->indexOf(obsStyle)); + return; } void StyleEditor::removeStyle() { @@ -84,11 +84,11 @@ void StyleEditor::removeStyle() { return; } -void StyleEditor::styleSelected( const QString& /*text*/ ) -{ +void StyleEditor::styleSelected( const QString& /*text*/ ) { QModelIndex proxyIndex( styleProxyModel->index(styleComboBox->currentIndex(),0) ); QModelIndex sourceIndex( styleProxyModel->mapToSource(proxyIndex) ); setStyle( styleListModel->at(sourceIndex.row()) ); + return; } void StyleEditor::save() { @@ -103,18 +103,18 @@ 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->toSI().quantity ); - this->obsStyle->setOgMax( lineEdit_ogMax->toSI().quantity ); - this->obsStyle->setFgMin( lineEdit_fgMin->toSI().quantity ); - this->obsStyle->setFgMax( lineEdit_fgMax->toSI().quantity ); - this->obsStyle->setIbuMin( lineEdit_ibuMin->toSI().quantity ); - this->obsStyle->setIbuMax( lineEdit_ibuMax->toSI().quantity ); - this->obsStyle->setColorMin_srm( lineEdit_colorMin->toSI().quantity ); - this->obsStyle->setColorMax_srm( lineEdit_colorMax->toSI().quantity ); - this->obsStyle->setCarbMin_vol( lineEdit_carbMin->toSI().quantity ); - this->obsStyle->setCarbMax_vol( lineEdit_carbMax->toSI().quantity ); - this->obsStyle->setAbvMin_pct( lineEdit_abvMin->toSI().quantity ); - this->obsStyle->setAbvMax_pct( lineEdit_abvMax->toSI().quantity ); + 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->toCanonical().quantity() ); + this->obsStyle->setIbuMax( lineEdit_ibuMax->toCanonical().quantity() ); + 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->toCanonical().quantity() ); + this->obsStyle->setAbvMax_pct( lineEdit_abvMax->toCanonical().quantity() ); this->obsStyle->setProfile( textEdit_profile->toPlainText() ); this->obsStyle->setIngredients( textEdit_ingredients->toPlainText() ); this->obsStyle->setExamples( textEdit_examples->toPlainText() ); @@ -184,35 +184,22 @@ void StyleEditor::clear() textEdit_notes->setText(QString("")); } -void StyleEditor::showChanges(QMetaProperty* metaProp) -{ - bool updateAll = false; - QString propName; - QVariant val; +void StyleEditor::showChanges(QMetaProperty* metaProp) { Style *s = obsStyle; - if( s == 0 ) - { + if (s == 0 ) { clear(); return; } - if( metaProp == 0 ) - updateAll = true; - else - { - propName = metaProp->name(); - val = metaProp->read(s); - } - - if( updateAll ) - { + if (metaProp == 0) { + // updateAll = true; lineEdit_name->setText(s->name()); tabWidget_profile->setTabText(0, s->name() ); lineEdit_category->setText(s->category()); lineEdit_categoryNumber->setText(s->categoryNumber()); lineEdit_styleLetter->setText(s->styleLetter()); lineEdit_styleGuide->setText(s->styleGuide()); - comboBox_type->setCurrentIndex(s->type()); + comboBox_type->setCurrentIndex(static_cast(s->type())); lineEdit_ogMin->setText(s); lineEdit_ogMax->setText(s); lineEdit_fgMin->setText(s); @@ -233,7 +220,10 @@ void StyleEditor::showChanges(QMetaProperty* metaProp) return; } - if( propName == PropertyNames::NamedEntity::name ) { + QString propName = metaProp->name(); + QVariant val = metaProp->read(s); + + if (propName == PropertyNames::NamedEntity::name ) { lineEdit_name->setText(val.toString()); tabWidget_profile->setTabText(0, s->name() ); } else if( propName == PropertyNames::Style::category ) { diff --git a/src/UiAmountWithUnits.cpp b/src/UiAmountWithUnits.cpp index 912ecec63..0dc64eab9 100644 --- a/src/UiAmountWithUnits.cpp +++ b/src/UiAmountWithUnits.cpp @@ -31,7 +31,7 @@ #include "Localization.h" #include "measurement/Measurement.h" -#include "utils/OptionalToStream.h" +#include "utils/OptionalHelpers.h" UiAmountWithUnits::UiAmountWithUnits(QWidget * parent, BtFieldType fieldType, @@ -140,14 +140,14 @@ double UiAmountWithUnits::toDoubleRaw(bool * ok) const { // Make sure we get the right decimal point (. or ,) and the right grouping // separator (, or .). Some locales write 1.000,10 and other write // 1,000.10. We need to catch both - QString decimal = QRegExp::escape(QLocale::system().decimalPoint()); - QString grouping = QRegExp::escape(QLocale::system().groupSeparator()); + QString const decimal = QRegExp::escape(Localization::getLocale().decimalPoint()); + QString const grouping = QRegExp::escape(Localization::getLocale().groupSeparator()); QRegExp amtUnit; amtUnit.setPattern("((?:\\d+" + grouping + ")?\\d+(?:" + decimal + "\\d+)?|" + decimal + "\\d+)\\s*(\\w+)?"); amtUnit.setCaseSensitivity(Qt::CaseInsensitive); - // if the regex dies, return 0.0 + // If the regex dies, return 0.0 if (amtUnit.indexIn(this->getWidgetText()) == -1) { if (ok) { *ok = false; @@ -158,14 +158,14 @@ double UiAmountWithUnits::toDoubleRaw(bool * ok) const { return Localization::toDouble(amtUnit.cap(1), Q_FUNC_INFO); } -Measurement::Amount UiAmountWithUnits::toSI() { +Measurement::Amount UiAmountWithUnits::toCanonical() { // It's a coding error to call this function if we are not dealing with a physical quantity // However, we can often recover by returning just the numeric part of the field and pretending it is seconds. // If caller ignores the units and just used the quantity, then it will probably be OK. Of course, we log an error - // and hope this prompts caller to call toDoubleRaw() instead of toSI().quantity! + // and hope this prompts caller to call toDoubleRaw() instead of toCanonical().quantity()! if (!std::holds_alternative(this->getFieldType())) { qCritical() << - Q_FUNC_INFO << "Coding error - call to toSI() for" << this->getFieldType() << "for field with contents:" << + Q_FUNC_INFO << "Coding error - call to toCanonical() for" << this->getFieldType() << "for field with contents:" << this->getWidgetText(); return Measurement::Amount{this->getWidgetText().toDouble(), Measurement::Units::seconds}; } @@ -217,7 +217,7 @@ void UiAmountWithUnits::textOrUnitsChanged(PreviousScaleInfo previousScaleInfo) if (physicalQuantity == Measurement::PhysicalQuantity::Color) { precision = 0; } - correctedText = this->displayAmount(amountAsCanonical.quantity, precision); + 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; diff --git a/src/UiAmountWithUnits.h b/src/UiAmountWithUnits.h index 1bd270e6b..606cbdc1a 100644 --- a/src/UiAmountWithUnits.h +++ b/src/UiAmountWithUnits.h @@ -126,7 +126,7 @@ class UiAmountWithUnits { /** * \brief Returns the field converted to canonical units for the relevant \c Measurement::PhysicalQuantity */ - Measurement::Amount toSI(); + Measurement::Amount toCanonical(); /** * \brief Use this when you want to do something with the returned QString diff --git a/src/WaterDialog.cpp b/src/WaterDialog.cpp index c21eec560..ee4361d72 100644 --- a/src/WaterDialog.cpp +++ b/src/WaterDialog.cpp @@ -456,22 +456,22 @@ double WaterDialog::calculateGristpH() { if ( m_rec && m_rec->fermentables().size() ) { - double platoRatio = 1/Measurement::Units::plato.fromSI(m_rec->og()); + double platoRatio = 1/Measurement::Units::plato.fromCanonical(m_rec->og()); double color = m_rec->color_srm(); double colorFromGrain = 0.0; for (Fermentable * i : m_rec->fermentables()) { - // I am counting anything that doesn't have diastatic - // power as a roasted/crystal malt. I am sure my assumption will - // haunt me later, but I have no way of knowing what kind of malt - // (base, crystal, roasted) this is. + // I am counting anything that doesn't have diastatic + // power as a roasted/crystal malt. I am sure my assumption will + // haunt me later, but I have no way of knowing what kind of malt + // (base, crystal, roasted) this is. if ( i->diastaticPower_lintner() < 1 ) { - double lovi = 19.0; + double lovi = 19.0; if ( i->color_srm() <= 120 ) { lovi = ( i->color_srm() + 0.6)/1.35; - } + } colorFromGrain = ( i->amount_kg() / m_total_grains ) * lovi; - } + } } double colorRatio = colorFromGrain/m_weighted_colors; pHAdjustment = platoRatio * ( pHSlopeLight * (1-colorRatio) + pHSlopeDark * colorRatio) *color; diff --git a/src/WaterEditor.cpp b/src/WaterEditor.cpp index 9bb603fcc..4f7b29870 100644 --- a/src/WaterEditor.cpp +++ b/src/WaterEditor.cpp @@ -242,15 +242,15 @@ void WaterEditor::inputFieldModified() { // feels wrong that we just set both from the same input, but probably needs some more profound thought // about what exactly correct behaviour should be. if (signalSender == this->comboBox_alk) {this->pimpl->editedWater->setAlkalinityAsHCO3(this->comboBox_alk->currentText() == QString("HCO3"));} - else if (signalSender == this->lineEdit_alk) {this->pimpl->editedWater->setBicarbonate_ppm (this->lineEdit_alk->toSI().quantity); // NB continues on next line! - this->pimpl->editedWater->setAlkalinity (this->lineEdit_alk->toSI().quantity); } - else if (signalSender == this->lineEdit_ca) {this->pimpl->editedWater->setCalcium_ppm (this->lineEdit_ca->toSI().quantity); } - else if (signalSender == this->lineEdit_cl) {this->pimpl->editedWater->setChloride_ppm (this->lineEdit_cl->toSI().quantity); } - else if (signalSender == this->lineEdit_mg) {this->pimpl->editedWater->setMagnesium_ppm (this->lineEdit_mg->toSI().quantity); } - else if (signalSender == this->lineEdit_na) {this->pimpl->editedWater->setSodium_ppm (this->lineEdit_na->toSI().quantity); } + else if (signalSender == this->lineEdit_alk) {this->pimpl->editedWater->setBicarbonate_ppm (this->lineEdit_alk->toCanonical().quantity()); // NB continues on next line! + this->pimpl->editedWater->setAlkalinity (this->lineEdit_alk->toCanonical().quantity()); } + else if (signalSender == this->lineEdit_ca) {this->pimpl->editedWater->setCalcium_ppm (this->lineEdit_ca->toCanonical().quantity()); } + else if (signalSender == this->lineEdit_cl) {this->pimpl->editedWater->setChloride_ppm (this->lineEdit_cl->toCanonical().quantity()); } + else if (signalSender == this->lineEdit_mg) {this->pimpl->editedWater->setMagnesium_ppm (this->lineEdit_mg->toCanonical().quantity()); } + else if (signalSender == this->lineEdit_na) {this->pimpl->editedWater->setSodium_ppm (this->lineEdit_na->toCanonical().quantity()); } else if (signalSender == this->lineEdit_name) {this->pimpl->editedWater->setName (this->lineEdit_name->text()); } - else if (signalSender == this->lineEdit_ph) {this->pimpl->editedWater->setPh (this->lineEdit_ph->toSI().quantity); } - else if (signalSender == this->lineEdit_so4) {this->pimpl->editedWater->setSulfate_ppm (this->lineEdit_so4->toSI().quantity); } + else if (signalSender == this->lineEdit_ph) {this->pimpl->editedWater->setPh (this->lineEdit_ph->toCanonical().quantity()); } + else if (signalSender == this->lineEdit_so4) {this->pimpl->editedWater->setSulfate_ppm (this->lineEdit_so4->toCanonical().quantity()); } else if (signalSender == this->plainTextEdit_notes) {this->pimpl->editedWater->setNotes (this->plainTextEdit_notes->toPlainText()); } else { // If we get here, it's probably a coding error diff --git a/src/YeastEditor.cpp b/src/YeastEditor.cpp index b0bdc42d7..c99573db3 100644 --- a/src/YeastEditor.cpp +++ b/src/YeastEditor.cpp @@ -69,10 +69,10 @@ void YeastEditor::save() { this->obsYeast->setLaboratory(lineEdit_laboratory->text()); this->obsYeast->setProductID(lineEdit_productID->text()); - this->obsYeast->setMinTemperature_c(lineEdit_minTemperature->toSI().quantity); - this->obsYeast->setMaxTemperature_c(lineEdit_maxTemperature->toSI().quantity); + 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->toSI().quantity); + this->obsYeast->setAttenuation_pct(lineEdit_attenuation->toCanonical().quantity()); this->obsYeast->setTimesCultured(lineEdit_timesCultured->text().toInt()); this->obsYeast->setMaxReuse(lineEdit_maxReuse->text().toInt()); @@ -126,13 +126,13 @@ void YeastEditor::showChanges(QMetaProperty * metaProp) { } } if (propName == PropertyNames::Yeast::type || updateAll) { - comboBox_type->setCurrentIndex(obsYeast->type()); + comboBox_type->setCurrentIndex(static_cast(obsYeast->type())); if (!updateAll) { return; } } if (propName == PropertyNames::Yeast::form || updateAll) { - comboBox_form->setCurrentIndex(obsYeast->form()); + comboBox_form->setCurrentIndex(static_cast(obsYeast->form())); if (!updateAll) { return; } @@ -176,7 +176,7 @@ void YeastEditor::showChanges(QMetaProperty * metaProp) { } } if (propName == PropertyNames::Yeast::flocculation || updateAll) { - comboBox_flocculation->setCurrentIndex(obsYeast->flocculation()); + comboBox_flocculation->setCurrentIndex(static_cast(obsYeast->flocculation())); if (!updateAll) { return; } diff --git a/src/YeastSortFilterProxyModel.cpp b/src/YeastSortFilterProxyModel.cpp index ef0306cd6..8aa775c84 100644 --- a/src/YeastSortFilterProxyModel.cpp +++ b/src/YeastSortFilterProxyModel.cpp @@ -40,7 +40,7 @@ bool YeastSortFilterProxyModel::lessThan(const QModelIndex &left, switch (left.column()) { case YEASTINVENTORYCOL: - if (Measurement::qStringToSI(leftYeast.toString(), Measurement::PhysicalQuantity::Volume).quantity == 0.0 && + if (Measurement::qStringToSI(leftYeast.toString(), Measurement::PhysicalQuantity::Volume).quantity() == 0.0 && this->sortOrder() == Qt::AscendingOrder) { return false; } diff --git a/src/measurement/Amount.cpp b/src/measurement/Amount.cpp index ab65daba3..1a86d03ea 100644 --- a/src/measurement/Amount.cpp +++ b/src/measurement/Amount.cpp @@ -1,6 +1,6 @@ /* * measurement/Amount.cpp is part of Brewtarget, and is copyright the following - * authors 2022: + * authors 2022-2023: * - Matt Young * * Brewtarget is free software: you can redistribute it and/or modify @@ -23,35 +23,126 @@ #include "measurement/Unit.h" +namespace Measurement { + + Amount::Amount(double quantity, Unit const & unit) : m_quantity{quantity}, m_unit{&unit} { + return; + }; + + //! Copy constructor + Amount::Amount(Amount const & other) = default; + + //! Assignment operator + Amount & Amount::operator=(Amount const & other) = default; + + //! Move constructor. + Amount::Amount(Amount && other) = default; + + //! Move assignment. + Amount & Amount::operator=(Amount && other) = default; + + double Amount::quantity() const { return this->m_quantity; } + Unit const * Amount::unit() const { return this->m_unit ; } + + void Amount::setQuantity(double const val) { this->m_quantity = val; return; } + void Amount::setUnit (Unit const & val) { this->m_unit = &val; return; } + +} + +// 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) { - return lhs.quantity < rhs.quantity; + if (lhs.unit() == rhs.unit()) { + return lhs.quantity() < rhs.quantity(); } // It's a coding error if we try to compare two things that aren't a measure of the same physical quantity (because // it's meaningless to compare a temperature to a mass, etc - Q_ASSERT(lhs.unit.getPhysicalQuantity() == rhs.unit.getPhysicalQuantity()); + Q_ASSERT(lhs.unit()->getPhysicalQuantity() == rhs.unit()->getPhysicalQuantity()); - return lhs.unit.toSI(lhs.quantity).quantity < rhs.unit.toSI(lhs.quantity).quantity; + return lhs.unit()->toCanonical(lhs.quantity()).quantity() < rhs.unit()->toCanonical(lhs.quantity()).quantity(); } bool operator==(Measurement::Amount const & lhs, Measurement::Amount const & rhs) { // Amounts in the same units are trivial to compare - if (lhs.unit == rhs.unit) { - return lhs.quantity == rhs.quantity; + if (lhs.unit() == rhs.unit()) { + return lhs.quantity() == rhs.quantity(); } // It's a coding error if we try to compare two things that aren't a measure of the same physical quantity (because // it's meaningless to compare a temperature to a mass, etc - Q_ASSERT(lhs.unit.getPhysicalQuantity() == rhs.unit.getPhysicalQuantity()); + Q_ASSERT(lhs.unit()->getPhysicalQuantity() == rhs.unit()->getPhysicalQuantity()); - return lhs.unit.toSI(lhs.quantity).quantity == rhs.unit.toSI(lhs.quantity).quantity; + return lhs.unit()->toCanonical(lhs.quantity()).quantity() == rhs.unit()->toCanonical(lhs.quantity()).quantity(); } template S & operator<<(S & stream, Measurement::Amount const amount) { - stream << amount.quantity << " " << amount.unit.name; + stream << amount.quantity() << " " << amount.unit()->name; return stream; } diff --git a/src/measurement/Amount.h b/src/measurement/Amount.h index 378004831..030b3743f 100644 --- a/src/measurement/Amount.h +++ b/src/measurement/Amount.h @@ -1,6 +1,6 @@ /* * measurement/Amount.h is part of Brewtarget, and is copyright the following - * authors 2022: + * authors 2022-2023: * - Matt Young * * Brewtarget is free software: you can redistribute it and/or modify @@ -31,12 +31,70 @@ namespace Measurement { * know what units we're getting back from a function (eg \c qstringToSI), it helps reduce bugs to have * quantity and units together in a single struct. */ - struct Amount { - double quantity; - Unit const & unit; + class Amount { + public: + //! Regular constructor + Amount(double quantity, Unit const & unit); + + //! Copy constructor + Amount(Amount const & other); + + //! Assignment operator + Amount & operator=(Amount const & other); + + //! Move constructor. + Amount(Amount && other); + + //! Move assignment. + Amount & operator=(Amount && other); + + double quantity() const; + Unit const * unit () const; + + private: + void setQuantity(double const val); + void setUnit (Unit const & val); + + protected: + double m_quantity; + Unit const * m_unit; }; } +/** + * \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/IbuMethods.cpp b/src/measurement/IbuMethods.cpp index 3a168c399..3c25c0f49 100644 --- a/src/measurement/IbuMethods.cpp +++ b/src/measurement/IbuMethods.cpp @@ -1,6 +1,6 @@ /* * measurement/IbuMethods.cpp is part of Brewtarget, and is copyright the following - * authors 2009-2022: + * authors 2009-2023: * - Daniel Pettersson * - Mattias Måhl * - Matt Young @@ -66,8 +66,8 @@ namespace { double finalVolume_liters, double wort_grav, double minutes) { - double volumeFactor = (Measurement::Units::us_gallons.toSI(5.0).quantity)/ finalVolume_liters; - double hopsFactor = hops_grams/ (Measurement::Units::ounces.toSI(1.0).quantity * 1000.0); + double volumeFactor = (Measurement::Units::us_gallons.toCanonical(5.0).quantity())/ finalVolume_liters; + double hopsFactor = hops_grams/ (Measurement::Units::ounces.toCanonical(1.0).quantity() * 1000.0); static Polynomial p(Polynomial() << 0.7000029428 << -0.08868853463 << 0.02720809386 << -0.002340415323 << 0.00009925450081 << -0.000002102006144 << 0.00000002132644293 << -0.00000000008229488217); //using 60 minutes as a general table diff --git a/src/measurement/Measurement.cpp b/src/measurement/Measurement.cpp index 1ee8996bd..34365a939 100644 --- a/src/measurement/Measurement.cpp +++ b/src/measurement/Measurement.cpp @@ -1,6 +1,6 @@ /* * measurement/Measurement.cpp is part of Brewtarget, and is copyright the following - * authors 2010-2022: + * authors 2010-2023: * - Mark de Wever * - Matt Young * - Mik Firestone @@ -32,7 +32,7 @@ #include "model/Style.h" // For PropertyNames::Style::colorMin_srm, PropertyNames::Style::colorMax_srm #include "PersistentSettings.h" #include "utils/BtStringConst.h" -#include "utils/OptionalToStream.h" +#include "utils/OptionalHelpers.h" namespace { @@ -163,13 +163,13 @@ QString Measurement::displayAmount(Measurement::Amount const & amount, std::optional forcedSystemOfMeasurement, std::optional forcedScale) { // Check for insane values. - if (Algorithms::isNan(amount.quantity) || Algorithms::isInf(amount.quantity)) { + if (Algorithms::isNan(amount.quantity()) || Algorithms::isInf(amount.quantity())) { return "-"; } // If the caller told us (via forced system of measurement) what UnitSystem to use, use that, otherwise get whatever // one we're using generally for related physical property. - PhysicalQuantity const physicalQuantity = amount.unit.getPhysicalQuantity(); + PhysicalQuantity const physicalQuantity = amount.unit()->getPhysicalQuantity(); Measurement::UnitSystem const & displayUnitSystem = forcedSystemOfMeasurement ? UnitSystem::getInstance(*forcedSystemOfMeasurement, physicalQuantity) : Measurement::getDisplayUnitSystem(physicalQuantity); @@ -220,13 +220,13 @@ double Measurement::amountDisplay(Measurement::Amount const & amount, std::optional forcedScale) { // Check for insane values. - if (Algorithms::isNan(amount.quantity) || Algorithms::isInf(amount.quantity)) { + if (Algorithms::isNan(amount.quantity()) || Algorithms::isInf(amount.quantity())) { return -1.0; } // If the caller told us (via forced system of measurement) what UnitSystem to use, use that, otherwise get whatever // one we're using generally for related physical property. - PhysicalQuantity const physicalQuantity = amount.unit.getPhysicalQuantity(); + PhysicalQuantity const physicalQuantity = amount.unit()->getPhysicalQuantity(); Measurement::UnitSystem const & displayUnitSystem = forcedSystemOfMeasurement ? UnitSystem::getInstance(*forcedSystemOfMeasurement, physicalQuantity) : Measurement::getDisplayUnitSystem(physicalQuantity); @@ -311,8 +311,8 @@ QString Measurement::displayThickness( double thick_lkg, bool showUnits ) { Measurement::Unit const * weightUnit; Measurement::getThicknessUnits(&volUnit, &weightUnit); - double num = volUnit->fromSI(thick_lkg); - double den = weightUnit->fromSI(1.0); + double num = volUnit->fromCanonical(thick_lkg); + double den = weightUnit->fromCanonical(1.0); if (showUnits) { return QString("%L1 %2/%3").arg(num/den, fieldWidth, format, precision).arg(volUnit->name).arg(weightUnit->name); @@ -325,17 +325,22 @@ Measurement::Amount Measurement::qStringToSI(QString qstr, Measurement::PhysicalQuantity const physicalQuantity, std::optional forcedSystemOfMeasurement, std::optional forcedScale) { + qDebug() << + Q_FUNC_INFO << "Input" << qstr << "of" << physicalQuantity << "; forcedSystemOfMeasurement=" << + forcedSystemOfMeasurement << "; forcedScale=" << forcedScale; + // // If the caller told us that the SystemOfMeasurement and/or RelativeScale on the input (qstr) are "forced" then that // information can be used to interpret a case where no (valid) unit is supplied in the input (ie it's just a number // rather than number plus units) or where the supplied unit is ambiguous (eg US pints are different than Imperial // pints). Otherwise, just otherwise get whatever UnitSystem we're using generally for related physical property. // - Measurement::UnitSystem const & displayUnitSystem = + Measurement::UnitSystem const & displayUnitSystem { forcedSystemOfMeasurement ? UnitSystem::getInstance(*forcedSystemOfMeasurement, physicalQuantity) : - Measurement::getDisplayUnitSystem(physicalQuantity); + Measurement::getDisplayUnitSystem(physicalQuantity) + }; Measurement::Unit const * defaultUnit { - forcedScale ? displayUnitSystem.scaleUnit(*forcedScale) : &Measurement::getUnitForInternalStorage(physicalQuantity) + forcedScale ? displayUnitSystem.scaleUnit(*forcedScale) : displayUnitSystem.unit() }; // It's a coding error if defaultUnit is null, because it means previousScaleInfo.oldForcedScale was not valid for // oldUnitSystem. However, we can recover. diff --git a/src/measurement/Measurement.h b/src/measurement/Measurement.h index edb27e20d..95cb80ae7 100644 --- a/src/measurement/Measurement.h +++ b/src/measurement/Measurement.h @@ -1,6 +1,6 @@ /* * measurement/Measurement.h is part of Brewtarget, and is copyright the following - * authors 2010-2022: + * authors 2010-2023: * - Mark de Wever * - Matt Young * - Mik Firestone @@ -104,7 +104,7 @@ namespace Measurement { * * \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 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, @@ -191,6 +191,11 @@ namespace Measurement { 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, diff --git a/src/measurement/PhysicalQuantity.cpp b/src/measurement/PhysicalQuantity.cpp index e21595caa..815257fb3 100644 --- a/src/measurement/PhysicalQuantity.cpp +++ b/src/measurement/PhysicalQuantity.cpp @@ -24,19 +24,20 @@ namespace { EnumStringMapping const physicalQuantityToName { - {QObject::tr("Mass" ), Measurement::PhysicalQuantity::Mass }, - {QObject::tr("Volume" ), Measurement::PhysicalQuantity::Volume }, - {QObject::tr("Time" ), Measurement::PhysicalQuantity::Time }, - {QObject::tr("Temperature" ), Measurement::PhysicalQuantity::Temperature }, - {QObject::tr("Color" ), Measurement::PhysicalQuantity::Color }, - {QObject::tr("Density" ), Measurement::PhysicalQuantity::Density }, - {QObject::tr("Mixed" ), Measurement::PhysicalQuantity::Mixed }, - {QObject::tr("DiastaticPower"), Measurement::PhysicalQuantity::DiastaticPower}, - {QObject::tr("Acidity" ), Measurement::PhysicalQuantity::Acidity }, - {QObject::tr("Bitterness" ), Measurement::PhysicalQuantity::Bitterness }, - {QObject::tr("Carbonation" ), Measurement::PhysicalQuantity::Carbonation }, - {QObject::tr("Concentration" ), Measurement::PhysicalQuantity::Concentration }, - {QObject::tr("Viscosity" ), Measurement::PhysicalQuantity::Viscosity }, + {QObject::tr("Mass" ), Measurement::PhysicalQuantity::Mass }, + {QObject::tr("Volume" ), Measurement::PhysicalQuantity::Volume }, + {QObject::tr("Time" ), Measurement::PhysicalQuantity::Time }, + {QObject::tr("Temperature" ), Measurement::PhysicalQuantity::Temperature }, + {QObject::tr("Color" ), Measurement::PhysicalQuantity::Color }, + {QObject::tr("Density" ), Measurement::PhysicalQuantity::Density }, + {QObject::tr("Mixed" ), Measurement::PhysicalQuantity::Mixed }, + {QObject::tr("Diastatic Power" ), Measurement::PhysicalQuantity::DiastaticPower }, + {QObject::tr("Acidity" ), Measurement::PhysicalQuantity::Acidity }, + {QObject::tr("Bitterness" ), Measurement::PhysicalQuantity::Bitterness }, + {QObject::tr("Carbonation" ), Measurement::PhysicalQuantity::Carbonation }, + {QObject::tr("Concentration" ), Measurement::PhysicalQuantity::Concentration }, + {QObject::tr("Viscosity" ), Measurement::PhysicalQuantity::Viscosity }, + {QObject::tr("Specific Heat Capacity"), Measurement::PhysicalQuantity::SpecificHeatCapacity}, }; } diff --git a/src/measurement/PhysicalQuantity.h b/src/measurement/PhysicalQuantity.h index a1a712a1c..954e64e26 100644 --- a/src/measurement/PhysicalQuantity.h +++ b/src/measurement/PhysicalQuantity.h @@ -1,6 +1,6 @@ /* * measurement/PhysicalQuantity.h is part of Brewtarget, and is copyright the following - * authors 2021-2022: + * authors 2021-2023: * - Matt Young * * Brewtarget is free software: you can redistribute it and/or modify @@ -112,7 +112,9 @@ namespace Measurement { // separate. Carbonation, Concentration, - Viscosity + Viscosity, + // Specific heat capacity -- see https://en.wikipedia.org/wiki/Specific_heat_capacity + SpecificHeatCapacity, }; /** diff --git a/src/measurement/SystemOfMeasurement.cpp b/src/measurement/SystemOfMeasurement.cpp index 722651460..f8f0799d4 100644 --- a/src/measurement/SystemOfMeasurement.cpp +++ b/src/measurement/SystemOfMeasurement.cpp @@ -27,42 +27,48 @@ namespace { // One day we should probably combine these two mapping tables // EnumStringMapping const systemOfMeasurementToDisplayName { - {QObject::tr("British Imperial" ), Measurement::SystemOfMeasurement::Imperial }, - {QObject::tr("US Customary" ), Measurement::SystemOfMeasurement::UsCustomary }, - {QObject::tr("Metric" ), Measurement::SystemOfMeasurement::Metric }, - {QObject::tr("Metric Alternate" ), Measurement::SystemOfMeasurement::MetricAlternate }, - {QObject::tr("Universal Standard" ), Measurement::SystemOfMeasurement::UniversalStandard }, - {QObject::tr("SRM (Standard Reference Method)" ), Measurement::SystemOfMeasurement::StandardReferenceMethod }, - {QObject::tr("EBC (European Brewery Convention)"), Measurement::SystemOfMeasurement::EuropeanBreweryConvention }, - {QObject::tr("Lovibond" ), Measurement::SystemOfMeasurement::Lovibond }, - {QObject::tr("SG (Specific Gravity)" ), Measurement::SystemOfMeasurement::SpecificGravity }, - {QObject::tr("Plato" ), Measurement::SystemOfMeasurement::Plato }, - {QObject::tr("Brix" ), Measurement::SystemOfMeasurement::Brix }, - {QObject::tr("Lintner" ), Measurement::SystemOfMeasurement::Lintner }, - {QObject::tr("Windisch Kolbach" ), Measurement::SystemOfMeasurement::WindischKolbach }, - {QObject::tr("Carbonation Volumes" ), Measurement::SystemOfMeasurement::CarbonationVolumes }, - {QObject::tr("Carbonation Mass Per Volume" ), Measurement::SystemOfMeasurement::CarbonationMassPerVolume }, - {QObject::tr("Concentration Parts Per" ), Measurement::SystemOfMeasurement::ConcentrationPartsPer }, - {QObject::tr("Concentration Mass Per Volume" ), Measurement::SystemOfMeasurement::ConcentrationMassPerVolume}, + {QObject::tr("British Imperial" ), Measurement::SystemOfMeasurement::Imperial }, + {QObject::tr("US Customary" ), Measurement::SystemOfMeasurement::UsCustomary }, + {QObject::tr("Metric" ), Measurement::SystemOfMeasurement::Metric }, + {QObject::tr("Metric Alternate" ), Measurement::SystemOfMeasurement::MetricAlternate }, + {QObject::tr("Universal Standard" ), Measurement::SystemOfMeasurement::UniversalStandard }, + {QObject::tr("SRM (Standard Reference Method)" ), Measurement::SystemOfMeasurement::StandardReferenceMethod }, + {QObject::tr("EBC (European Brewery Convention)" ), Measurement::SystemOfMeasurement::EuropeanBreweryConvention }, + {QObject::tr("Lovibond" ), Measurement::SystemOfMeasurement::Lovibond }, + {QObject::tr("SG (Specific Gravity)" ), Measurement::SystemOfMeasurement::SpecificGravity }, + {QObject::tr("Plato" ), Measurement::SystemOfMeasurement::Plato }, + {QObject::tr("Brix" ), Measurement::SystemOfMeasurement::Brix }, + {QObject::tr("Lintner" ), Measurement::SystemOfMeasurement::Lintner }, + {QObject::tr("Windisch Kolbach" ), Measurement::SystemOfMeasurement::WindischKolbach }, + {QObject::tr("Carbonation Volumes" ), Measurement::SystemOfMeasurement::CarbonationVolumes }, + {QObject::tr("Carbonation Mass Per Volume" ), Measurement::SystemOfMeasurement::CarbonationMassPerVolume }, + {QObject::tr("Concentration Parts Per" ), Measurement::SystemOfMeasurement::ConcentrationPartsPer }, + {QObject::tr("Concentration Mass Per Volume" ), Measurement::SystemOfMeasurement::ConcentrationMassPerVolume }, + {QObject::tr("Specific Heat Capacity Calories per"), Measurement::SystemOfMeasurement::SpecificHeatCapacityCalories}, + {QObject::tr("Specific Heat Capacity Joules per" ), Measurement::SystemOfMeasurement::SpecificHeatCapacityJoules }, + + }; EnumStringMapping const systemOfMeasurementToUniqueName { - {"Imperial" , Measurement::SystemOfMeasurement::Imperial }, - {"UsCustomary" , Measurement::SystemOfMeasurement::UsCustomary }, - {"Metric" , Measurement::SystemOfMeasurement::Metric }, - {"MetricAlternate" , Measurement::SystemOfMeasurement::MetricAlternate }, - {"UniversalStandard" , Measurement::SystemOfMeasurement::UniversalStandard }, - {"StandardReferenceMethod" , Measurement::SystemOfMeasurement::StandardReferenceMethod }, - {"EuropeanBreweryConvention" , Measurement::SystemOfMeasurement::EuropeanBreweryConvention }, - {"Lovibond" , Measurement::SystemOfMeasurement::Lovibond }, - {"SpecificGravity" , Measurement::SystemOfMeasurement::SpecificGravity }, - {"Plato" , Measurement::SystemOfMeasurement::Plato }, - {"Brix" , Measurement::SystemOfMeasurement::Brix }, - {"Lintner" , Measurement::SystemOfMeasurement::Lintner }, - {"WindischKolbach" , Measurement::SystemOfMeasurement::WindischKolbach }, - {"CarbonationVolumes" , Measurement::SystemOfMeasurement::CarbonationVolumes }, - {"CarbonationMassPerVolume" , Measurement::SystemOfMeasurement::CarbonationMassPerVolume }, - {"ConcentrationPartsPer" , Measurement::SystemOfMeasurement::ConcentrationPartsPer }, - {"ConcentrationMassPerVolume", Measurement::SystemOfMeasurement::ConcentrationMassPerVolume}, + {"Imperial" , Measurement::SystemOfMeasurement::Imperial }, + {"UsCustomary" , Measurement::SystemOfMeasurement::UsCustomary }, + {"Metric" , Measurement::SystemOfMeasurement::Metric }, + {"MetricAlternate" , Measurement::SystemOfMeasurement::MetricAlternate }, + {"UniversalStandard" , Measurement::SystemOfMeasurement::UniversalStandard }, + {"StandardReferenceMethod" , Measurement::SystemOfMeasurement::StandardReferenceMethod }, + {"EuropeanBreweryConvention" , Measurement::SystemOfMeasurement::EuropeanBreweryConvention }, + {"Lovibond" , Measurement::SystemOfMeasurement::Lovibond }, + {"SpecificGravity" , Measurement::SystemOfMeasurement::SpecificGravity }, + {"Plato" , Measurement::SystemOfMeasurement::Plato }, + {"Brix" , Measurement::SystemOfMeasurement::Brix }, + {"Lintner" , Measurement::SystemOfMeasurement::Lintner }, + {"WindischKolbach" , Measurement::SystemOfMeasurement::WindischKolbach }, + {"CarbonationVolumes" , Measurement::SystemOfMeasurement::CarbonationVolumes }, + {"CarbonationMassPerVolume" , Measurement::SystemOfMeasurement::CarbonationMassPerVolume }, + {"ConcentrationPartsPer" , Measurement::SystemOfMeasurement::ConcentrationPartsPer }, + {"ConcentrationMassPerVolume" , Measurement::SystemOfMeasurement::ConcentrationMassPerVolume }, + {"SpecificHeatCapacityCalories", Measurement::SystemOfMeasurement::SpecificHeatCapacityCalories}, + {"SpecificHeatCapacityJoules" , Measurement::SystemOfMeasurement::SpecificHeatCapacityJoules }, }; } diff --git a/src/measurement/SystemOfMeasurement.h b/src/measurement/SystemOfMeasurement.h index b36c9cf9b..01068d086 100644 --- a/src/measurement/SystemOfMeasurement.h +++ b/src/measurement/SystemOfMeasurement.h @@ -1,6 +1,6 @@ /* * measurement/SystemOfMeasurement.h is part of Brewtarget, and is copyright the following - * authors 2022: + * authors 2022-2023: * - Matt Young * * Brewtarget is free software: you can redistribute it and/or modify @@ -133,9 +133,13 @@ namespace Measurement { // // See https://en.wikipedia.org/wiki/Parts-per_notation for more info ConcentrationPartsPer, - ConcentrationMassPerVolume + ConcentrationMassPerVolume, // See comment in measurement/Unit.h about the different viscosity units + + // Specific Heat Capacity + SpecificHeatCapacityCalories, + SpecificHeatCapacityJoules, }; diff --git a/src/measurement/Unit.cpp b/src/measurement/Unit.cpp index d7e0be2c7..792ccb775 100644 --- a/src/measurement/Unit.cpp +++ b/src/measurement/Unit.cpp @@ -1,6 +1,6 @@ /* * measurement/Unit.cpp is part of Brewtarget, and is copyright the following - * authors 2009-2022: + * authors 2009-2023: * - Mark de Wever * - Matt Young * - Mik Firestone @@ -42,8 +42,8 @@ namespace { // Make sure we get the right decimal point (. or ,) and the right grouping // separator (, or .). Some locales write 1.000,10 and other write // 1,000.10. We need to catch both - QString decimal = QRegExp::escape(QLocale::system().decimalPoint()); - QString grouping = QRegExp::escape(QLocale::system().groupSeparator()); + QString const decimal = QRegExp::escape(Localization::getLocale().decimalPoint()); + QString const grouping = QRegExp::escape(Localization::getLocale().groupSeparator()); amtUnit.setPattern("((?:\\d+" + grouping + ")?\\d+(?:" + decimal + "\\d+)?|" + decimal + "\\d+)\\s*(\\w+)?"); amtUnit.setCaseSensitivity(Qt::CaseInsensitive); @@ -159,11 +159,11 @@ Measurement::Unit const & Measurement::Unit::getCanonical() const { return Measurement::Unit::getCanonicalUnit(this->getPhysicalQuantity()); } -Measurement::Amount Measurement::Unit::toSI(double amt) const { +Measurement::Amount Measurement::Unit::toCanonical(double amt) const { return Measurement::Amount{this->pimpl->convertToCanonical(amt), this->getCanonical()}; } -double Measurement::Unit::fromSI(double amt) const { +double Measurement::Unit::fromCanonical(double amt) const { return this->pimpl->convertFromCanonical(amt); } @@ -196,7 +196,7 @@ QString Measurement::Unit::convert(QString qstr, QString toUnit) { double si; if (f) { double amt = valueFromString(qstr); - si = f->toSI(amt).quantity; + si = f->toCanonical(amt).quantity(); } else { si = 0.0; } @@ -209,7 +209,7 @@ QString Measurement::Unit::convert(QString qstr, QString toUnit) { return QString("%1 ?").arg(Measurement::displayQuantity(si, 3)); } - return QString("%1 %2").arg(Measurement::displayQuantity(u->fromSI(si), 3)).arg(toUnit); + return QString("%1 %2").arg(Measurement::displayQuantity(u->fromCanonical(si), 3)).arg(toUnit); } Measurement::Unit const * Measurement::Unit::getUnit(QString const & name, @@ -365,4 +365,9 @@ namespace Measurement::Units { // Yes, 1 centipoise = 1 millipascal-second, but a poise and a pascal-second are NOT equal so we have two different units Unit const centipoise {Measurement::UnitSystems::viscosity_Metric, QObject::tr("cP"), [](double x){return x;}, [](double y){return y;}, 1.0}; Unit const millipascalSecond {Measurement::UnitSystems::viscosity_MetricAlternate, QObject::tr("mPa-s"), [](double x){return x;}, [](double y){return y;}, 1.0, ¢ipoise}; + // == Specific heat capacity == + // See comment in measurement/Unith for why the non-metric units are the canonical ones + Unit const caloriesPerCelsiusPerGram{Measurement::UnitSystems::specificHeatCapacity_Calories, QObject::tr("c/g·C" ), [](double x){return x;}, [](double y){return y;}, 1.0}; + Unit const joulesPerKelvinPerKg {Measurement::UnitSystems::specificHeatCapacity_Joules , QObject::tr("J/kg·K"), [](double x){return x / 4184.0;}, [](double y){return y * 4184.0;}, 1.0, &caloriesPerCelsiusPerGram}; + } diff --git a/src/measurement/Unit.h b/src/measurement/Unit.h index 9dbfb639d..b14f93025 100644 --- a/src/measurement/Unit.h +++ b/src/measurement/Unit.h @@ -1,6 +1,6 @@ /* * measurement/Unit.h is part of Brewtarget, and is copyright the following - * authors 2009-2022: + * authors 2009-2023: * - Jeff Bailey * - Mark de Wever * - Matt Young @@ -92,19 +92,22 @@ namespace Measurement { QString const name; /** - * \brief Returns the canonical units we use for \c PhysicalQuantity this \c Unit relates to + * \brief Returns the canonical units we use for \c PhysicalQuantity this \c Unit relates to. These are the units + * we use for internal storage and (for the most part) for calculations. */ Measurement::Unit const & getCanonical() const; /** - * \brief Convert an amount of this unit to its canonical system of measurement (usually, but not always, an SI measure) + * \brief Convert an amount of this unit to its canonical system of measurement (usually, but not always, an SI or + * other metric measure) */ - Measurement::Amount toSI(double amt) const; + Measurement::Amount toCanonical(double amt) const; /** - * \brief Convert an amount of this unit from its canonical system of measurement (usually, but not always, an SI measure) + * \brief Convert an amount of this unit from its canonical system of measurement (usually, but not always, an SI + * or other metric measure) */ - double fromSI(double amt) const; + double fromCanonical(double amt) const; /** * \brief Returns the \c Measurement::PhysicalQuantity that this \c Measurement::Unit measures. This is a @@ -252,12 +255,31 @@ namespace Measurement { // to be much used in brewing, so we do not implement it.) extern Unit const centipoise; extern Unit const millipascalSecond; + // == Specific Heat Capacity == + // Per https://en.wikipedia.org/wiki/Specific_heat_capacity SI units are "joules per kelvin per kilogram" (which + // is the same as "joules per degree Celsius per kilogram"). However, historically a measurement involving + // calories instead of joules was used in chemistry, nutrition and, it seems, brewing. There are two types of + // calorie: + // - the "small calorie" (or "gram-calorie", "cal") = 4.184 J + // - The "grand calorie" (aka "kilocalorie", "kcal" or "Cal") = 1000 small calories = 4184 J + // + // However, the specific heat measurement using "cal" is the same as that for "Cal": + // 1 cal / (°C × g) = 1 Cal / (°C × kg) = 4184 J / (°K × kg) = the specific heat capacity of liquid water + // + // So, we only implement "calories per degree Celsius per gram" as it's identical to "kilocalories per degree + // Celsius per kilogram". + // + // NOTE: This is one instance where our "canonical" unit is NOT the metric one. Historically, the code has always + // used "calories per Celsius per gram" rather than "joules per Celsius per kilogram", including for storing + // amounts in the DB. Also the "calories" version is what is used by BeerJSON and BeerXML. + // + extern Unit const caloriesPerCelsiusPerGram; + extern Unit const joulesPerKelvinPerKg; } } //.:TODO:. "SpecificVolumeType": "Specific volume is the inverse of density, with units of volume over mass, ie qt/lb or L/kg. Commonly used for mash thickness.", - /** * \brief Convenience function to allow output of \c Measurement::Unit to \c QDebug or \c QTextStream stream etc */ diff --git a/src/measurement/UnitSystem.cpp b/src/measurement/UnitSystem.cpp index 56a2bdeb8..a3f1541a5 100644 --- a/src/measurement/UnitSystem.cpp +++ b/src/measurement/UnitSystem.cpp @@ -1,6 +1,6 @@ /* * measurement/UnitSystem.cpp is part of Brewtarget, and is copyright the following - * authors 2009-2022: + * authors 2009-2023: * - Jeff Bailey * - Matt Young * - Mik Firestone @@ -24,7 +24,6 @@ #include #include -#include #include #include "Localization.h" @@ -36,16 +35,6 @@ namespace { char const format = 'f'; int const defaultPrecision = 3; - // All functions in QRegExp are reentrant, so it should be safe to use as a shared const in multi-threaded code. - QRegExp const amtUnit { - // Make sure we get the right decimal point (. or ,) and the right grouping separator (, or .). Some locales - // write 1.000,10 and others write 1,000.10. We need to catch both. - "((?:\\d+" + QRegExp::escape(QLocale::system().groupSeparator()) + ")?\\d+(?:" + - QRegExp::escape(QLocale::system().decimalPoint()) + "\\d+)?|" + - QRegExp::escape(QLocale::system().decimalPoint()) + "\\d+)\\s*(\\w+)?", - Qt::CaseInsensitive - }; - QMultiMap physicalQuantityToUnitSystems; // Used by UnitSystem::getInstanceByName() @@ -128,16 +117,16 @@ class Measurement::UnitSystem::impl { std::pair displayableAmount(Measurement::Amount const & amount, std::optional forcedScale) const { // Special cases - if (amount.unit.getPhysicalQuantity() != this->physicalQuantity) { - return std::pair(amount.quantity, ""); + if (amount.unit()->getPhysicalQuantity() != this->physicalQuantity) { + return std::pair(amount.quantity(), ""); } - auto siAmount = amount.unit.toSI(amount.quantity); + auto siAmount = amount.unit()->toCanonical(amount.quantity()); // If there is only one unit in this unit system, then the scale to unit mapping will be empty as there's nothing // to choose from if (this->scaleToUnit.size() == 0) { - return std::pair(this->defaultUnit->fromSI(siAmount.quantity), this->defaultUnit->name); + return std::pair(this->defaultUnit->fromCanonical(siAmount.quantity()), this->defaultUnit->name); } // Conversely, if we have a non-empty mapping then it's a coding error if it only has one entry! @@ -148,7 +137,7 @@ class Measurement::UnitSystem::impl { // It's a coding error to specify a forced scale that is not in the UnitSystem Q_ASSERT(this->scaleToUnit.contains(*forcedScale)); Measurement::Unit const * bb = this->scaleToUnit.value(*forcedScale); - return std::pair(bb->fromSI(siAmount.quantity), bb->name); + return std::pair(bb->fromCanonical(siAmount.quantity()), bb->name); } // Search for the smallest measure in this system that's not too big to show the supplied value @@ -156,7 +145,7 @@ class Measurement::UnitSystem::impl { // (e.g., mg, g, kg). Measurement::Unit const * last = nullptr; for (auto it : this->scaleToUnit) { - if (last != nullptr && qAbs(siAmount.quantity) < it->toSI(it->boundary()).quantity) { + if (last != nullptr && qAbs(siAmount.quantity()) < it->toCanonical(it->boundary()).quantity()) { // Stop looping as we've found a unit that's too big to use (so we'll return the last one, ie the one smaller, // below) break; @@ -166,7 +155,7 @@ class Measurement::UnitSystem::impl { // It is a programming error if the map was empty (ie we didn't go through the loop at all) Q_ASSERT(last != nullptr); - return std::pair(last->fromSI(siAmount.quantity), last->name); + return std::pair(last->fromCanonical(siAmount.quantity()), last->name); } // Member variables for impl @@ -209,6 +198,15 @@ bool Measurement::UnitSystem::operator==(UnitSystem const & other) const { } Measurement::Amount Measurement::UnitSystem::qstringToSI(QString qstr, Unit const & defUnit) const { + // All functions in QRegExp are reentrant, so it should be safe to use as a shared const in multi-threaded code. + static QRegExp const amtUnit { + // Make sure we get the right decimal point (. or ,) and the right grouping separator (, or .). Some locales + // write 1.000,10 and others write 1,000.10. We need to catch both. + "((?:\\d+" + QRegExp::escape(Localization::getLocale().groupSeparator()) + ")?\\d+(?:" + + QRegExp::escape(Localization::getLocale().decimalPoint()) + "\\d+)?|" + + QRegExp::escape(Localization::getLocale().decimalPoint()) + "\\d+)\\s*(\\w+)?", + Qt::CaseInsensitive + }; // make sure we can parse the string if (amtUnit.indexIn(qstr) == -1) { @@ -248,10 +246,10 @@ Measurement::Amount Measurement::UnitSystem::qstringToSI(QString qstr, Unit cons unitToUse = &defUnit; } - Measurement::Amount siAmount = unitToUse->toSI(amt); + Measurement::Amount siAmount = unitToUse->toCanonical(amt); qDebug() << - Q_FUNC_INFO << this->uniqueName << ": " << qstr << "is" << amt << " " << unitToUse->name << "=" << siAmount.quantity << - "in" << siAmount.unit.name; + Q_FUNC_INFO << this->uniqueName << ": " << qstr << "is" << amt << " " << unitToUse->name << "=" << siAmount.quantity() << + "in" << siAmount.unit()->name; return siAmount; } @@ -267,7 +265,7 @@ QString Measurement::UnitSystem::displayAmount(Measurement::Amount const & amoun auto result = this->pimpl->displayableAmount(amount, forcedScale); if (result.second.isEmpty()) { - return QString("%L1").arg(this->amountDisplay(Measurement::Amount{result.first, amount.unit}, forcedScale), + return QString("%L1").arg(this->amountDisplay(Measurement::Amount{result.first, *amount.unit()}, forcedScale), fieldWidth, format, precision); @@ -543,4 +541,15 @@ namespace Measurement::UnitSystems { &Measurement::Units::millipascalSecond, "viscosity_MetricAlternate", Measurement::SystemOfMeasurement::MetricAlternate}; + + UnitSystem const specificHeatCapacity_Calories{PhysicalQuantity::SpecificHeatCapacity, + &Measurement::Units::caloriesPerCelsiusPerGram, + "specificHeatCapacity_Calories", + Measurement::SystemOfMeasurement::SpecificHeatCapacityCalories}; + + UnitSystem const specificHeatCapacity_Joules{PhysicalQuantity::SpecificHeatCapacity, + &Measurement::Units::joulesPerKelvinPerKg, + "specificHeatCapacity_Joules", + Measurement::SystemOfMeasurement::SpecificHeatCapacityJoules}; + } diff --git a/src/measurement/UnitSystem.h b/src/measurement/UnitSystem.h index c773a550b..87ae6815c 100644 --- a/src/measurement/UnitSystem.h +++ b/src/measurement/UnitSystem.h @@ -1,6 +1,6 @@ /* * measurement/UnitSystem.h is part of Brewtarget, and is copyright the following - * authors 2009-2022: + * authors 2009-2023: * - Jeff Bailey * - Matt Young * - Mik Firestone @@ -287,6 +287,11 @@ namespace Measurement { extern UnitSystem const viscosity_Metric; extern UnitSystem const viscosity_MetricAlternate; + // As explained in measurement/Unit.h, our canonical unit system for specific heat capacity is calories per ... + // rather than joules per ... + extern UnitSystem const specificHeatCapacity_Calories; + extern UnitSystem const specificHeatCapacity_Joules; + } } diff --git a/src/model/BrewNote.cpp b/src/model/BrewNote.cpp index c56539163..14fea8a71 100644 --- a/src/model/BrewNote.cpp +++ b/src/model/BrewNote.cpp @@ -22,7 +22,6 @@ #include #include -#include #include #include #include @@ -83,80 +82,82 @@ BrewNote::BrewNote(Recipe const & recipe) : return; } -BrewNote::BrewNote(NamedParameterBundle const & namedParameterBundle) : - NamedEntity{namedParameterBundle}, - loading {false}, - m_brewDate {namedParameterBundle(PropertyNames::BrewNote::brewDate ).toDate()}, - m_fermentDate {namedParameterBundle(PropertyNames::BrewNote::fermentDate ).toDate()}, - m_notes {namedParameterBundle(PropertyNames::BrewNote::notes ).toString()}, - m_sg {namedParameterBundle(PropertyNames::BrewNote::sg ).toDouble()}, - m_abv {namedParameterBundle(PropertyNames::BrewNote::abv ).toDouble()}, - m_effIntoBK_pct {namedParameterBundle(PropertyNames::BrewNote::effIntoBK_pct ).toDouble()}, - m_brewhouseEff_pct {namedParameterBundle(PropertyNames::BrewNote::brewhouseEff_pct ).toDouble()}, - m_volumeIntoBK_l {namedParameterBundle(PropertyNames::BrewNote::volumeIntoBK_l ).toDouble()}, - m_strikeTemp_c {namedParameterBundle(PropertyNames::BrewNote::strikeTemp_c ).toDouble()}, - m_mashFinTemp_c {namedParameterBundle(PropertyNames::BrewNote::mashFinTemp_c ).toDouble()}, - m_og {namedParameterBundle(PropertyNames::BrewNote::og ).toDouble()}, - m_postBoilVolume_l {namedParameterBundle(PropertyNames::BrewNote::postBoilVolume_l ).toDouble()}, - m_volumeIntoFerm_l {namedParameterBundle(PropertyNames::BrewNote::volumeIntoFerm_l ).toDouble()}, - m_pitchTemp_c {namedParameterBundle(PropertyNames::BrewNote::pitchTemp_c ).toDouble()}, - m_fg {namedParameterBundle(PropertyNames::BrewNote::fg ).toDouble()}, - m_attenuation {namedParameterBundle(PropertyNames::BrewNote::attenuation ).toDouble()}, - m_finalVolume_l {namedParameterBundle(PropertyNames::BrewNote::finalVolume_l ).toDouble()}, - m_boilOff_l {namedParameterBundle(PropertyNames::BrewNote::boilOff_l ).toDouble()}, - m_projBoilGrav {namedParameterBundle(PropertyNames::BrewNote::projBoilGrav ).toDouble()}, - m_projVolIntoBK_l {namedParameterBundle(PropertyNames::BrewNote::projVolIntoBK_l ).toDouble()}, - m_projStrikeTemp_c {namedParameterBundle(PropertyNames::BrewNote::projStrikeTemp_c ).toDouble()}, - m_projMashFinTemp_c{namedParameterBundle(PropertyNames::BrewNote::projMashFinTemp_c).toDouble()}, - m_projOg {namedParameterBundle(PropertyNames::BrewNote::projOg ).toDouble()}, - m_projVolIntoFerm_l{namedParameterBundle(PropertyNames::BrewNote::projVolIntoFerm_l).toDouble()}, - m_projFg {namedParameterBundle(PropertyNames::BrewNote::projFg ).toDouble()}, - m_projEff_pct {namedParameterBundle(PropertyNames::BrewNote::projEff_pct ).toDouble()}, - m_projABV_pct {namedParameterBundle(PropertyNames::BrewNote::projABV_pct ).toDouble()}, - m_projPoints {namedParameterBundle(PropertyNames::BrewNote::projPoints ).toDouble()}, - m_projFermPoints {namedParameterBundle(PropertyNames::BrewNote::projFermPoints ).toDouble()}, - m_projAtten {namedParameterBundle(PropertyNames::BrewNote::projAtten ).toDouble()}, - m_recipeId {namedParameterBundle(PropertyNames::BrewNote::recipeId ).toInt()} { +BrewNote::BrewNote(QDate dateNow, QString const & name) : + NamedEntity {name, true}, + loading {false }, + m_brewDate {dateNow}, + m_fermentDate { }, + m_notes {"" }, + m_sg {0.0 }, + m_abv {0.0 }, + m_effIntoBK_pct {0.0 }, + m_brewhouseEff_pct {0.0 }, + m_volumeIntoBK_l {0.0 }, + m_strikeTemp_c {0.0 }, + m_mashFinTemp_c {0.0 }, + m_og {0.0 }, + m_postBoilVolume_l {0.0 }, + m_volumeIntoFerm_l {0.0 }, + m_pitchTemp_c {0.0 }, + m_fg {0.0 }, + m_attenuation {0.0 }, + m_finalVolume_l {0.0 }, + m_boilOff_l {0.0 }, + m_projBoilGrav {0.0 }, + m_projVolIntoBK_l {0.0 }, + m_projStrikeTemp_c {0.0 }, + m_projMashFinTemp_c{0.0 }, + m_projOg {0.0 }, + m_projVolIntoFerm_l{0.0 }, + m_projFg {0.0 }, + m_projEff_pct {0.0 }, + m_projABV_pct {0.0 }, + m_projPoints {0.0 }, + m_projFermPoints {0.0 }, + m_projAtten {0.0 }, + m_recipeId {-1 } { return; } - -BrewNote::BrewNote(QDate dateNow, QString const & name) : - NamedEntity {name, true}, +BrewNote::BrewNote(NamedParameterBundle const & namedParameterBundle) : + NamedEntity{namedParameterBundle}, loading {false}, - m_brewDate {dateNow}, - m_fermentDate {}, - m_notes {""}, - m_sg {0.0}, - m_abv {0.0}, - m_effIntoBK_pct {0.0}, - m_brewhouseEff_pct {0.0}, - m_volumeIntoBK_l {0.0}, - m_strikeTemp_c {0.0}, - m_mashFinTemp_c {0.0}, - m_og {0.0}, - m_postBoilVolume_l {0.0}, - m_volumeIntoFerm_l {0.0}, - m_pitchTemp_c {0.0}, - m_fg {0.0}, - m_attenuation {0.0}, - m_finalVolume_l {0.0}, - m_boilOff_l {0.0}, - m_projBoilGrav {0.0}, - m_projVolIntoBK_l {0.0}, - m_projStrikeTemp_c {0.0}, - m_projMashFinTemp_c{0.0}, - m_projOg {0.0}, - m_projVolIntoFerm_l{0.0}, - m_projFg {0.0}, - m_projEff_pct {0.0}, - m_projABV_pct {0.0}, - m_projPoints {0.0}, - m_projFermPoints {0.0}, - m_projAtten {0.0} { + m_brewDate {namedParameterBundle.val(PropertyNames::BrewNote::brewDate )}, + m_fermentDate {namedParameterBundle.val(PropertyNames::BrewNote::fermentDate )}, + m_notes {namedParameterBundle.val(PropertyNames::BrewNote::notes )}, + m_sg {namedParameterBundle.val(PropertyNames::BrewNote::sg )}, + m_abv {namedParameterBundle.val(PropertyNames::BrewNote::abv )}, + m_effIntoBK_pct {namedParameterBundle.val(PropertyNames::BrewNote::effIntoBK_pct )}, + m_brewhouseEff_pct {namedParameterBundle.val(PropertyNames::BrewNote::brewhouseEff_pct )}, + m_volumeIntoBK_l {namedParameterBundle.val(PropertyNames::BrewNote::volumeIntoBK_l )}, + m_strikeTemp_c {namedParameterBundle.val(PropertyNames::BrewNote::strikeTemp_c )}, + m_mashFinTemp_c {namedParameterBundle.val(PropertyNames::BrewNote::mashFinTemp_c )}, + m_og {namedParameterBundle.val(PropertyNames::BrewNote::og )}, + m_postBoilVolume_l {namedParameterBundle.val(PropertyNames::BrewNote::postBoilVolume_l )}, + m_volumeIntoFerm_l {namedParameterBundle.val(PropertyNames::BrewNote::volumeIntoFerm_l )}, + m_pitchTemp_c {namedParameterBundle.val(PropertyNames::BrewNote::pitchTemp_c )}, + m_fg {namedParameterBundle.val(PropertyNames::BrewNote::fg )}, + m_attenuation {namedParameterBundle.val(PropertyNames::BrewNote::attenuation )}, + m_finalVolume_l {namedParameterBundle.val(PropertyNames::BrewNote::finalVolume_l )}, + m_boilOff_l {namedParameterBundle.val(PropertyNames::BrewNote::boilOff_l )}, + m_projBoilGrav {namedParameterBundle.val(PropertyNames::BrewNote::projBoilGrav )}, + m_projVolIntoBK_l {namedParameterBundle.val(PropertyNames::BrewNote::projVolIntoBK_l )}, + m_projStrikeTemp_c {namedParameterBundle.val(PropertyNames::BrewNote::projStrikeTemp_c )}, + m_projMashFinTemp_c{namedParameterBundle.val(PropertyNames::BrewNote::projMashFinTemp_c)}, + m_projOg {namedParameterBundle.val(PropertyNames::BrewNote::projOg )}, + m_projVolIntoFerm_l{namedParameterBundle.val(PropertyNames::BrewNote::projVolIntoFerm_l)}, + m_projFg {namedParameterBundle.val(PropertyNames::BrewNote::projFg )}, + m_projEff_pct {namedParameterBundle.val(PropertyNames::BrewNote::projEff_pct )}, + m_projABV_pct {namedParameterBundle.val(PropertyNames::BrewNote::projABV_pct )}, + m_projPoints {namedParameterBundle.val(PropertyNames::BrewNote::projPoints )}, + m_projFermPoints {namedParameterBundle.val(PropertyNames::BrewNote::projFermPoints )}, + m_projAtten {namedParameterBundle.val(PropertyNames::BrewNote::projAtten )}, + m_recipeId {namedParameterBundle.val(PropertyNames::BrewNote::recipeId )} { return; } +BrewNote::~BrewNote() = default; + void BrewNote::populateNote(Recipe* parent) { this->m_recipeId = parent->key(); diff --git a/src/model/BrewNote.h b/src/model/BrewNote.h index 132e9bddc..0cc2e658b 100644 --- a/src/model/BrewNote.h +++ b/src/model/BrewNote.h @@ -35,37 +35,37 @@ //====================================================================================================================== //========================================== Start of property name constants ========================================== #define AddPropertyName(property) namespace PropertyNames::BrewNote { BtStringConst const property{#property}; } -AddPropertyName(abv) -AddPropertyName(attenuation) -AddPropertyName(boilOff_l) -AddPropertyName(brewDate) -AddPropertyName(brewhouseEff_pct) -AddPropertyName(effIntoBK_pct) -AddPropertyName(fermentDate) -AddPropertyName(fg) -AddPropertyName(finalVolume_l) -AddPropertyName(mashFinTemp_c) -AddPropertyName(notes) -AddPropertyName(og) -AddPropertyName(pitchTemp_c) -AddPropertyName(postBoilVolume_l) -AddPropertyName(projABV_pct) -AddPropertyName(projAtten) -AddPropertyName(projBoilGrav) -AddPropertyName(projEff_pct) -AddPropertyName(projFermPoints) -AddPropertyName(projFg) +AddPropertyName(abv ) +AddPropertyName(attenuation ) +AddPropertyName(boilOff_l ) +AddPropertyName(brewDate ) +AddPropertyName(brewhouseEff_pct ) +AddPropertyName(effIntoBK_pct ) +AddPropertyName(fermentDate ) +AddPropertyName(fg ) +AddPropertyName(finalVolume_l ) +AddPropertyName(mashFinTemp_c ) +AddPropertyName(notes ) +AddPropertyName(og ) +AddPropertyName(pitchTemp_c ) +AddPropertyName(postBoilVolume_l ) +AddPropertyName(projABV_pct ) +AddPropertyName(projAtten ) +AddPropertyName(projBoilGrav ) +AddPropertyName(projEff_pct ) +AddPropertyName(projFermPoints ) +AddPropertyName(projFg ) AddPropertyName(projMashFinTemp_c) -AddPropertyName(projOg) -AddPropertyName(projPoints) -AddPropertyName(projStrikeTemp_c) -AddPropertyName(projVolIntoBK_l) +AddPropertyName(projOg ) +AddPropertyName(projPoints ) +AddPropertyName(projStrikeTemp_c ) +AddPropertyName(projVolIntoBK_l ) AddPropertyName(projVolIntoFerm_l) -AddPropertyName(recipeId) -AddPropertyName(sg) -AddPropertyName(strikeTemp_c) -AddPropertyName(volumeIntoBK_l) -AddPropertyName(volumeIntoFerm_l) +AddPropertyName(recipeId ) +AddPropertyName(sg ) +AddPropertyName(strikeTemp_c ) +AddPropertyName(volumeIntoBK_l ) +AddPropertyName(volumeIntoFerm_l ) #undef AddPropertyName //=========================================== End of property name constants =========================================== //====================================================================================================================== @@ -88,7 +88,7 @@ class BrewNote : public NamedEntity { BrewNote(NamedParameterBundle const & namedParameterBundle); BrewNote(BrewNote const & other); - virtual ~BrewNote() = default; + virtual ~BrewNote(); bool operator<(BrewNote const & other) const; bool operator>(BrewNote const & other) const; diff --git a/src/model/Equipment.cpp b/src/model/Equipment.cpp index 22926edd7..e3e1f355d 100644 --- a/src/model/Equipment.cpp +++ b/src/model/Equipment.cpp @@ -56,26 +56,24 @@ Equipment::Equipment(QString t_name) : NamedEntity {t_name, true}, m_boilSize_l {22.927}, m_batchSize_l {18.927}, - m_tunVolume_l {0.0}, - m_tunWeight_kg {0.0}, - m_tunSpecificHeat_calGC{0.0}, - m_topUpWater_l {0.0}, - m_trubChillerLoss_l {1.0}, - m_evapRate_pctHr {0.0}, - m_evapRate_lHr {4.0}, - m_boilTime_min {60.0}, - m_calcBoilVolume {true}, - m_lauterDeadspace_l {0.0}, - m_topUpKettle_l {0.0}, - m_hopUtilization_pct {100.0}, - m_notes {""}, - m_grainAbsorption_LKg {1.086}, - m_boilingPoint_c {100.0} { + m_tunVolume_l {0.0 }, + m_tunWeight_kg {0.0 }, + m_tunSpecificHeat_calGC{0.0 }, + m_topUpWater_l {0.0 }, + m_trubChillerLoss_l {1.0 }, + m_evapRate_pctHr {0.0 }, + m_evapRate_lHr {4.0 }, + m_boilTime_min {60.0 }, + m_calcBoilVolume {true }, + m_lauterDeadspace_l {0.0 }, + m_topUpKettle_l {0.0 }, + m_hopUtilization_pct {100.0 }, + m_notes {"" }, + m_grainAbsorption_LKg {1.086 }, + m_boilingPoint_c {100.0 } { return; } - - // The default values below are set for the following fields that are not part of BeerXML 1.0 standard and so will // not be present in BeerXML files (unless we wrote them) but will be present in the database: // - evapRate_lHr @@ -84,23 +82,23 @@ Equipment::Equipment(QString t_name) : // Equipment::Equipment(NamedParameterBundle const & namedParameterBundle) : NamedEntity{namedParameterBundle}, - m_boilSize_l {namedParameterBundle(PropertyNames::Equipment::boilSize_l ).toDouble()}, - m_batchSize_l {namedParameterBundle(PropertyNames::Equipment::batchSize_l ).toDouble()}, - m_tunVolume_l {namedParameterBundle(PropertyNames::Equipment::tunVolume_l ).toDouble()}, - m_tunWeight_kg {namedParameterBundle(PropertyNames::Equipment::tunWeight_kg ).toDouble()}, - m_tunSpecificHeat_calGC{namedParameterBundle(PropertyNames::Equipment::tunSpecificHeat_calGC).toDouble()}, - m_topUpWater_l {namedParameterBundle(PropertyNames::Equipment::topUpWater_l ).toDouble()}, - m_trubChillerLoss_l {namedParameterBundle(PropertyNames::Equipment::trubChillerLoss_l ).toDouble()}, - m_evapRate_pctHr {namedParameterBundle(PropertyNames::Equipment::evapRate_pctHr ).toDouble()}, - m_evapRate_lHr {namedParameterBundle(PropertyNames::Equipment::evapRate_lHr, 4.0) }, - m_boilTime_min {namedParameterBundle(PropertyNames::Equipment::boilTime_min ).toDouble()}, - m_calcBoilVolume {namedParameterBundle(PropertyNames::Equipment::calcBoilVolume ).toBool() }, - m_lauterDeadspace_l {namedParameterBundle(PropertyNames::Equipment::lauterDeadspace_l ).toDouble()}, - m_topUpKettle_l {namedParameterBundle(PropertyNames::Equipment::topUpKettle_l ).toDouble()}, - m_hopUtilization_pct {namedParameterBundle(PropertyNames::Equipment::hopUtilization_pct ).toDouble()}, - m_notes {namedParameterBundle(PropertyNames::Equipment::notes ).toString()}, - m_grainAbsorption_LKg {namedParameterBundle(PropertyNames::Equipment::grainAbsorption_LKg, 1.086) }, - m_boilingPoint_c {namedParameterBundle(PropertyNames::Equipment::boilingPoint_c, 100.0) } { + m_boilSize_l {namedParameterBundle.val(PropertyNames::Equipment::boilSize_l )}, + m_batchSize_l {namedParameterBundle.val(PropertyNames::Equipment::batchSize_l )}, + m_tunVolume_l {namedParameterBundle.val(PropertyNames::Equipment::tunVolume_l )}, + m_tunWeight_kg {namedParameterBundle.val(PropertyNames::Equipment::tunWeight_kg )}, + m_tunSpecificHeat_calGC{namedParameterBundle.val(PropertyNames::Equipment::tunSpecificHeat_calGC )}, + m_topUpWater_l {namedParameterBundle.val(PropertyNames::Equipment::topUpWater_l )}, + m_trubChillerLoss_l {namedParameterBundle.val(PropertyNames::Equipment::trubChillerLoss_l )}, + m_evapRate_pctHr {namedParameterBundle.val(PropertyNames::Equipment::evapRate_pctHr )}, + m_evapRate_lHr {namedParameterBundle.val(PropertyNames::Equipment::evapRate_lHr, 4.0 )}, + m_boilTime_min {namedParameterBundle.val(PropertyNames::Equipment::boilTime_min )}, + m_calcBoilVolume {namedParameterBundle.val(PropertyNames::Equipment::calcBoilVolume )}, + m_lauterDeadspace_l {namedParameterBundle.val(PropertyNames::Equipment::lauterDeadspace_l )}, + m_topUpKettle_l {namedParameterBundle.val(PropertyNames::Equipment::topUpKettle_l )}, + m_hopUtilization_pct {namedParameterBundle.val(PropertyNames::Equipment::hopUtilization_pct )}, + m_notes {namedParameterBundle.val(PropertyNames::Equipment::notes )}, + m_grainAbsorption_LKg {namedParameterBundle.val(PropertyNames::Equipment::grainAbsorption_LKg, 1.086)}, + m_boilingPoint_c {namedParameterBundle.val(PropertyNames::Equipment::boilingPoint_c, 100.0)} { return; } @@ -126,6 +124,8 @@ Equipment::Equipment(Equipment const & other) : return; } +Equipment::~Equipment() = default; + //============================"SET" METHODS===================================== // The logic through here is similar to what's in Hop. Unfortunately, the additional signals don't allow quite the diff --git a/src/model/Equipment.h b/src/model/Equipment.h index a61a2b804..7cf5e66c5 100644 --- a/src/model/Equipment.h +++ b/src/model/Equipment.h @@ -1,6 +1,6 @@ /* * model/Equipment.h is part of Brewtarget, and is Copyright the following - * authors 2009-2021 + * authors 2009-2023 * - Jeff Bailey * - Matt Young * - Mik Firestone @@ -31,23 +31,23 @@ //====================================================================================================================== //========================================== Start of property name constants ========================================== #define AddPropertyName(property) namespace PropertyNames::Equipment { BtStringConst const property{#property}; } -AddPropertyName(boilTime_min) -AddPropertyName(boilSize_l) -AddPropertyName(batchSize_l) +AddPropertyName(batchSize_l ) +AddPropertyName(boilingPoint_c ) +AddPropertyName(boilSize_l ) +AddPropertyName(boilTime_min ) +AddPropertyName(calcBoilVolume ) +AddPropertyName(evapRate_lHr ) +AddPropertyName(evapRate_pctHr ) +AddPropertyName(grainAbsorption_LKg ) +AddPropertyName(hopUtilization_pct ) +AddPropertyName(lauterDeadspace_l ) +AddPropertyName(notes ) +AddPropertyName(topUpKettle_l ) +AddPropertyName(topUpWater_l ) +AddPropertyName(trubChillerLoss_l ) AddPropertyName(tunSpecificHeat_calGC) -AddPropertyName(tunWeight_kg) -AddPropertyName(notes) -AddPropertyName(boilingPoint_c) -AddPropertyName(grainAbsorption_LKg) -AddPropertyName(hopUtilization_pct) -AddPropertyName(topUpKettle_l) -AddPropertyName(lauterDeadspace_l) -AddPropertyName(calcBoilVolume) -AddPropertyName(evapRate_lHr) -AddPropertyName(evapRate_pctHr) -AddPropertyName(trubChillerLoss_l) -AddPropertyName(topUpWater_l) -AddPropertyName(tunVolume_l) +AddPropertyName(tunVolume_l ) +AddPropertyName(tunWeight_kg ) #undef AddPropertyName //=========================================== End of property name constants =========================================== //====================================================================================================================== @@ -61,17 +61,15 @@ AddPropertyName(tunVolume_l) class Equipment : public NamedEntity { Q_OBJECT +public: Q_CLASSINFO("signal", "equipments") - friend class EquipmentEditor; - -public: Equipment(QString t_name = ""); Equipment(NamedParameterBundle const & namedParameterBundle); Equipment(Equipment const & other); - virtual ~Equipment() = default; + virtual ~Equipment(); //! \brief The boil size in liters. Q_PROPERTY( double boilSize_l READ boilSize_l WRITE setBoilSize_l NOTIFY changedBoilSize_l ) @@ -81,7 +79,7 @@ class Equipment : public NamedEntity { Q_PROPERTY( double tunVolume_l READ tunVolume_l WRITE setTunVolume_l NOTIFY changedTunVolume_l ) //! \brief Set the tun mass in kg. Q_PROPERTY( double tunWeight_kg READ tunWeight_kg WRITE setTunWeight_kg NOTIFY changedTunWeight_kg ) - //! \brief Set the tun specific heat in kcal/(g*C) + //! \brief Set the tun specific heat in cal/(g*C) Q_PROPERTY( double tunSpecificHeat_calGC READ tunSpecificHeat_calGC WRITE setTunSpecificHeat_calGC NOTIFY changedTunSpecificHeat_calGC ) //! \brief Set the top-up water in liters. Q_PROPERTY( double topUpWater_l READ topUpWater_l WRITE setTopUpWater_l NOTIFY changedTopUpWater_l ) @@ -94,7 +92,7 @@ class Equipment : public NamedEntity { //! \brief Set the boil time in minutes. Q_PROPERTY( double boilTime_min READ boilTime_min WRITE setBoilTime_min NOTIFY changedBoilTime_min ) //! \brief Set whether you want the boil volume to be automatically calculated. - Q_PROPERTY( bool calcBoilVolume READ calcBoilVolume WRITE setCalcBoilVolume NOTIFY changedCalcBoilVolume ) + Q_PROPERTY( bool calcBoilVolume READ calcBoilVolume WRITE setCalcBoilVolume NOTIFY changedCalcBoilVolume ) //! \brief Set the lauter tun's deadspace in liters. Q_PROPERTY( double lauterDeadspace_l READ lauterDeadspace_l WRITE setLauterDeadspace_l NOTIFY changedLauterDeadspace_l ) //! \brief Set the kettle top up in liters. @@ -128,23 +126,23 @@ class Equipment : public NamedEntity { void setBoilingPoint_c(double var); // Get - double boilSize_l() const; - double batchSize_l() const; - double tunVolume_l() const; - double tunWeight_kg() const; - double tunSpecificHeat_calGC() const; - double topUpWater_l() const; - double trubChillerLoss_l() const; - double evapRate_pctHr() const; - double evapRate_lHr() const; - double boilTime_min() const; - bool calcBoilVolume() const; - double lauterDeadspace_l() const; - double topUpKettle_l() const; - double hopUtilization_pct() const; - QString notes() const; - double grainAbsorption_LKg(); - double boilingPoint_c() const; + double boilSize_l () const; + double batchSize_l () const; + double tunVolume_l () const; + double tunWeight_kg () const; + double tunSpecificHeat_calGC() const; + double topUpWater_l () const; + double trubChillerLoss_l () const; + double evapRate_pctHr () const; + double evapRate_lHr () const; + double boilTime_min () const; + bool calcBoilVolume () const; + double lauterDeadspace_l () const; + double topUpKettle_l () const; + double hopUtilization_pct () const; + QString notes () const; + double grainAbsorption_LKg (); + double boilingPoint_c () const; //! \brief Calculate how much wort is left immediately at knockout. double wortEndOfBoil_l( double kettleWort_l ) const; diff --git a/src/model/Fermentable.cpp b/src/model/Fermentable.cpp index 6677c70d6..591890393 100644 --- a/src/model/Fermentable.cpp +++ b/src/model/Fermentable.cpp @@ -1,6 +1,6 @@ /* * model/Fermentable.cpp is part of Brewtarget, and is Copyright the following - * authors 2009-2021 + * authors 2009-2023 * - Kregg K * - Matt Young * - Mik Firestone @@ -79,23 +79,23 @@ Fermentable::Fermentable(QString name) : Fermentable::Fermentable(NamedParameterBundle const & namedParameterBundle) : NamedEntityWithInventory{namedParameterBundle}, - m_typeStr {QString() }, - m_type {static_cast(namedParameterBundle(PropertyNames::Fermentable::type).toInt())}, - m_amountKg {namedParameterBundle(PropertyNames::Fermentable::amount_kg ).toDouble()}, - m_yieldPct {namedParameterBundle(PropertyNames::Fermentable::yield_pct ).toDouble()}, - m_colorSrm {namedParameterBundle(PropertyNames::Fermentable::color_srm ).toDouble()}, - m_isAfterBoil {namedParameterBundle(PropertyNames::Fermentable::addAfterBoil ).toBool() }, - m_origin {namedParameterBundle(PropertyNames::Fermentable::origin, QString()) }, - m_supplier {namedParameterBundle(PropertyNames::Fermentable::supplier, QString()) }, - m_notes {namedParameterBundle(PropertyNames::Fermentable::notes, QString()) }, - m_coarseFineDiff{namedParameterBundle(PropertyNames::Fermentable::coarseFineDiff_pct ).toDouble()}, - m_moisturePct {namedParameterBundle(PropertyNames::Fermentable::moisture_pct ).toDouble()}, - m_diastaticPower{namedParameterBundle(PropertyNames::Fermentable::diastaticPower_lintner).toDouble()}, - m_proteinPct {namedParameterBundle(PropertyNames::Fermentable::protein_pct ).toDouble()}, - m_maxInBatchPct {namedParameterBundle(PropertyNames::Fermentable::maxInBatch_pct ).toDouble()}, - m_recommendMash {namedParameterBundle(PropertyNames::Fermentable::recommendMash ).toBool() }, - m_ibuGalPerLb {namedParameterBundle(PropertyNames::Fermentable::ibuGalPerLb ).toDouble()}, - m_isMashed {namedParameterBundle(PropertyNames::Fermentable::isMashed, false) } { + m_typeStr {QString()}, + m_type {namedParameterBundle.val(PropertyNames::Fermentable::type )}, + m_amountKg {namedParameterBundle.val(PropertyNames::Fermentable::amount_kg )}, + m_yieldPct {namedParameterBundle.val(PropertyNames::Fermentable::yield_pct )}, + m_colorSrm {namedParameterBundle.val(PropertyNames::Fermentable::color_srm )}, + m_isAfterBoil {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 {namedParameterBundle.val(PropertyNames::Fermentable::coarseFineDiff_pct )}, + m_moisturePct {namedParameterBundle.val(PropertyNames::Fermentable::moisture_pct )}, + m_diastaticPower {namedParameterBundle.val(PropertyNames::Fermentable::diastaticPower_lintner )}, + m_proteinPct {namedParameterBundle.val(PropertyNames::Fermentable::protein_pct )}, + m_maxInBatchPct {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 )} { return; } @@ -121,6 +121,8 @@ Fermentable::Fermentable(Fermentable const & other) : return; } +Fermentable::~Fermentable() = default; + // Gets Fermentable::Type Fermentable::type() const { return m_type; } diff --git a/src/model/Fermentable.h b/src/model/Fermentable.h index 61195fdc1..07a4c2107 100644 --- a/src/model/Fermentable.h +++ b/src/model/Fermentable.h @@ -1,6 +1,6 @@ /* * model/Fermentable.h is part of Brewtarget, and is Copyright the following - * authors 2009-2021 + * authors 2009-2023 * - Jeff Bailey * - Kregg K * - Matt Young @@ -77,13 +77,15 @@ class Fermentable : public NamedEntityWithInventory { enum AdditionMethod {Mashed, Steeped, Not_Mashed}; //! \brief The addition time. enum AdditionTime {Normal, Late}; - Q_ENUMS( Type AdditionMethod AdditionTime ) + Q_ENUM(Type) + Q_ENUM(AdditionMethod) + Q_ENUM(AdditionTime) Fermentable(QString name = ""); Fermentable(NamedParameterBundle const & namedParameterBundle); Fermentable(Fermentable const & other); - virtual ~Fermentable() = default; + virtual ~Fermentable(); //! \brief The \c Type. Q_PROPERTY( Type type READ type WRITE setType /*NOTIFY changed*/ /*changedType*/ ) diff --git a/src/model/Hop.cpp b/src/model/Hop.cpp index 323182acd..a2e67324e 100644 --- a/src/model/Hop.cpp +++ b/src/model/Hop.cpp @@ -1,6 +1,6 @@ /* * model/Hop.cpp is part of Brewtarget, and is Copyright the following - * authors 2009-2021 + * authors 2009-2023 * - Kregg K * - Matt Young * - Mik Firestone @@ -115,21 +115,21 @@ Hop::Hop(QString name) : Hop::Hop(NamedParameterBundle const & namedParameterBundle) : NamedEntityWithInventory{namedParameterBundle}, - m_use {static_cast (namedParameterBundle(PropertyNames::Hop::use ).toInt())}, - m_type {static_cast(namedParameterBundle(PropertyNames::Hop::type).toInt())}, - m_form {static_cast(namedParameterBundle(PropertyNames::Hop::form).toInt())}, - m_alpha_pct {namedParameterBundle(PropertyNames::Hop::alpha_pct ).toDouble()}, - m_amount_kg {namedParameterBundle(PropertyNames::Hop::amount_kg ).toDouble()}, - m_time_min {namedParameterBundle(PropertyNames::Hop::time_min ).toDouble()}, - m_notes {namedParameterBundle(PropertyNames::Hop::notes ).toString()}, - m_beta_pct {namedParameterBundle(PropertyNames::Hop::beta_pct ).toDouble()}, - m_hsi_pct {namedParameterBundle(PropertyNames::Hop::hsi_pct ).toDouble()}, - m_origin {namedParameterBundle(PropertyNames::Hop::origin ).toString()}, - m_substitutes {namedParameterBundle(PropertyNames::Hop::substitutes ).toString()}, - m_humulene_pct {namedParameterBundle(PropertyNames::Hop::humulene_pct ).toDouble()}, - m_caryophyllene_pct {namedParameterBundle(PropertyNames::Hop::caryophyllene_pct ).toDouble()}, - m_cohumulone_pct {namedParameterBundle(PropertyNames::Hop::cohumulone_pct ).toDouble()}, - m_myrcene_pct {namedParameterBundle(PropertyNames::Hop::myrcene_pct ).toDouble()} { + m_use {namedParameterBundle.val(PropertyNames::Hop::use )}, + m_type {namedParameterBundle.val(PropertyNames::Hop::type )}, + m_form {namedParameterBundle.val(PropertyNames::Hop::form )}, + m_alpha_pct {namedParameterBundle.val(PropertyNames::Hop::alpha_pct )}, + m_amount_kg {namedParameterBundle.val(PropertyNames::Hop::amount_kg )}, + m_time_min {namedParameterBundle.val(PropertyNames::Hop::time_min )}, + m_notes {namedParameterBundle.val(PropertyNames::Hop::notes )}, + m_beta_pct {namedParameterBundle.val(PropertyNames::Hop::beta_pct )}, + m_hsi_pct {namedParameterBundle.val(PropertyNames::Hop::hsi_pct )}, + m_origin {namedParameterBundle.val(PropertyNames::Hop::origin )}, + m_substitutes {namedParameterBundle.val(PropertyNames::Hop::substitutes )}, + m_humulene_pct {namedParameterBundle.val(PropertyNames::Hop::humulene_pct )}, + m_caryophyllene_pct {namedParameterBundle.val(PropertyNames::Hop::caryophyllene_pct )}, + m_cohumulone_pct {namedParameterBundle.val(PropertyNames::Hop::cohumulone_pct )}, + m_myrcene_pct {namedParameterBundle.val(PropertyNames::Hop::myrcene_pct )} { return; } diff --git a/src/model/Hop.h b/src/model/Hop.h index 6d5b0a08b..3ec6112b8 100644 --- a/src/model/Hop.h +++ b/src/model/Hop.h @@ -114,7 +114,9 @@ class Hop : public NamedEntityWithInventory { */ static QMap const useDisplayNames; - Q_ENUMS(Type Form Use) + Q_ENUM(Type) + Q_ENUM(Form) + Q_ENUM(Use) Hop(QString name = ""); Hop(NamedParameterBundle const & namedParameterBundle); diff --git a/src/model/Instruction.cpp b/src/model/Instruction.cpp index 239216335..578004616 100644 --- a/src/model/Instruction.cpp +++ b/src/model/Instruction.cpp @@ -1,6 +1,6 @@ /* * model/Instruction.cpp is part of Brewtarget, and is Copyright the following - * authors 2009-2021 + * authors 2009-2023 * - Matt Young * - Mik Firestone * - Philip Greggory Lee @@ -107,12 +107,12 @@ Instruction::Instruction(QString name) : Instruction::Instruction(NamedParameterBundle const & namedParameterBundle) : NamedEntity {namedParameterBundle}, - pimpl {new impl{*this}}, - m_directions{namedParameterBundle(PropertyNames::Instruction::directions).toString()}, - m_hasTimer {namedParameterBundle(PropertyNames::Instruction::hasTimer ).toBool()}, - m_timerValue{namedParameterBundle(PropertyNames::Instruction::timerValue).toString()}, - m_completed {namedParameterBundle(PropertyNames::Instruction::completed ).toBool()}, - m_interval {namedParameterBundle(PropertyNames::Instruction::interval ).toDouble()} { + pimpl {std::make_unique(*this)}, + m_directions{namedParameterBundle.val(PropertyNames::Instruction::directions)}, + m_hasTimer {namedParameterBundle.val(PropertyNames::Instruction::hasTimer )}, + m_timerValue{namedParameterBundle.val(PropertyNames::Instruction::timerValue)}, + m_completed {namedParameterBundle.val(PropertyNames::Instruction::completed )}, + m_interval {namedParameterBundle.val(PropertyNames::Instruction::interval )} { return; } diff --git a/src/model/Inventory.cpp b/src/model/Inventory.cpp index 637e96bd6..e06d9fa78 100644 --- a/src/model/Inventory.cpp +++ b/src/model/Inventory.cpp @@ -60,8 +60,8 @@ class Inventory::impl { } impl(NamedParameterBundle const & namedParameterBundle) : - id {namedParameterBundle(PropertyNames::Inventory::id).toInt() }, - amount{namedParameterBundle(PropertyNames::Inventory::amount).toDouble()} { + id {namedParameterBundle.val(PropertyNames::Inventory::id )}, + amount{namedParameterBundle.val(PropertyNames::Inventory::amount)} { return; } @@ -81,12 +81,12 @@ class Inventory::impl { }; -Inventory::Inventory() : pimpl{ new impl{} } { +Inventory::Inventory() : pimpl{std::make_unique()} { return; } Inventory::Inventory(NamedParameterBundle const & namedParameterBundle) : - pimpl{ new impl{namedParameterBundle} } { + pimpl{std::make_unique(namedParameterBundle)} { return; } @@ -123,13 +123,13 @@ void Inventory::setAmount(double amount) { return; } -void Inventory::setDeleted(bool var) { +void Inventory::setDeleted([[maybe_unused]] bool var) { // See comment in header. This is not currently implemented and it's therefore a coding error if it gets called Q_ASSERT(false); return; } -void Inventory::setDisplay(bool var) { +void Inventory::setDisplay([[maybe_unused]] bool var) { // See comment in header. This is not currently implemented and it's therefore a coding error if it gets called Q_ASSERT(false); return; @@ -142,14 +142,14 @@ void Inventory::hardDeleteOwnedEntities() { char const * InventoryFermentable::getIngredientClass() const { return "Fermentable"; } -char const * InventoryHop::getIngredientClass() const { return "Hop"; } -char const * InventoryMisc::getIngredientClass() const { return "Misc"; } -char const * InventoryYeast::getIngredientClass() const { return "Yeast"; } +char const * InventoryHop::getIngredientClass() const { return "Hop"; } +char const * InventoryMisc::getIngredientClass() const { return "Misc"; } +char const * InventoryYeast::getIngredientClass() const { return "Yeast"; } ObjectStore & InventoryFermentable::getObjectStoreTypedInstance() const { return ObjectStoreTyped::getInstance(); } -ObjectStore & InventoryHop::getObjectStoreTypedInstance() const { return ObjectStoreTyped::getInstance(); } -ObjectStore & InventoryMisc::getObjectStoreTypedInstance() const { return ObjectStoreTyped::getInstance(); } -ObjectStore & InventoryYeast::getObjectStoreTypedInstance() const { return ObjectStoreTyped::getInstance(); } +ObjectStore & InventoryHop::getObjectStoreTypedInstance() const { return ObjectStoreTyped::getInstance(); } +ObjectStore & InventoryMisc::getObjectStoreTypedInstance() const { return ObjectStoreTyped::getInstance(); } +ObjectStore & InventoryYeast::getObjectStoreTypedInstance() const { return ObjectStoreTyped::getInstance(); } template void InventoryUtils::setAmount(Ing & ing, double amount) { diff --git a/src/model/Mash.cpp b/src/model/Mash.cpp index 466cb4f39..591890438 100644 --- a/src/model/Mash.cpp +++ b/src/model/Mash.cpp @@ -86,14 +86,28 @@ ObjectStore & Mash::getObjectStoreTypedInstance() const { Mash::Mash(QString name) : NamedEntity{name, true}, pimpl{std::make_unique(*this)}, - m_grainTemp_c(0.0), - m_notes(QString()), - m_tunTemp_c(0.0), - m_spargeTemp_c(0.0), - m_ph(0.0), - m_tunWeight_kg(0.0), - m_tunSpecificHeat_calGC(0.0), - m_equipAdjust(true) { + m_grainTemp_c {0.0 }, + m_notes {"" }, + m_tunTemp_c {0.0 }, + m_spargeTemp_c {0.0 }, + m_ph {0.0 }, + m_tunWeight_kg {0.0 }, + m_tunSpecificHeat_calGC{0.0 }, + m_equipAdjust {true} { + return; +} + +Mash::Mash(NamedParameterBundle const & namedParameterBundle) : + NamedEntity {namedParameterBundle}, + pimpl{std::make_unique(*this)}, + m_grainTemp_c {namedParameterBundle.val(PropertyNames::Mash::grainTemp_c )}, + m_notes {namedParameterBundle.val(PropertyNames::Mash::notes )}, + m_tunTemp_c {namedParameterBundle.val(PropertyNames::Mash::tunTemp_c )}, + m_spargeTemp_c {namedParameterBundle.val(PropertyNames::Mash::spargeTemp_c )}, + m_ph {namedParameterBundle.val(PropertyNames::Mash::ph )}, + m_tunWeight_kg {namedParameterBundle.val(PropertyNames::Mash::tunWeight_kg )}, + m_tunSpecificHeat_calGC{namedParameterBundle.val(PropertyNames::Mash::tunSpecificHeat_calGC)}, + m_equipAdjust {namedParameterBundle.val(PropertyNames::Mash::equipAdjust )} { return; } @@ -134,21 +148,6 @@ Mash::Mash(Mash const & other) : return; } - -Mash::Mash(NamedParameterBundle const & namedParameterBundle) : - NamedEntity {namedParameterBundle}, - pimpl{std::make_unique(*this)}, - m_grainTemp_c {namedParameterBundle(PropertyNames::Mash::grainTemp_c ).toDouble()}, - m_notes {namedParameterBundle(PropertyNames::Mash::notes ).toString()}, - m_tunTemp_c {namedParameterBundle(PropertyNames::Mash::tunTemp_c ).toDouble()}, - m_spargeTemp_c {namedParameterBundle(PropertyNames::Mash::spargeTemp_c ).toDouble()}, - m_ph {namedParameterBundle(PropertyNames::Mash::ph ).toDouble()}, - m_tunWeight_kg {namedParameterBundle(PropertyNames::Mash::tunWeight_kg ).toDouble()}, - m_tunSpecificHeat_calGC{namedParameterBundle(PropertyNames::Mash::tunSpecificHeat_calGC).toDouble()}, - m_equipAdjust {namedParameterBundle(PropertyNames::Mash::equipAdjust ).toBool()} { - return; -} - // See https://herbsutter.com/gotw/_100/ for why we need to explicitly define the destructor here (and not in the // header file) Mash::~Mash() = default; @@ -361,7 +360,8 @@ QList< std::shared_ptr > Mash::mashSteps() const { return mashSteps; } -void Mash::acceptMashStepChange(QMetaProperty prop, QVariant /*val*/) { +void Mash::acceptMashStepChange([[maybe_unused]] QMetaProperty prop, + [[maybe_unused]] QVariant val) { MashStep* stepSender = qobject_cast(sender()); if (stepSender == nullptr) { return; diff --git a/src/model/MashStep.cpp b/src/model/MashStep.cpp index 8528cf5b4..8953fe40e 100644 --- a/src/model/MashStep.cpp +++ b/src/model/MashStep.cpp @@ -57,31 +57,31 @@ ObjectStore & MashStep::getObjectStoreTypedInstance() const { MashStep::MashStep(QString name) : NamedEntity {name, true}, - m_type {MashStep::Infusion}, - m_infuseAmount_l {0.0}, - m_stepTemp_c {0.0}, - m_stepTime_min {0.0}, - m_rampTime_min {0.0}, - m_endTemp_c {0.0}, - m_infuseTemp_c {0.0}, - m_decoctionAmount_l{0.0}, - m_stepNumber {0}, - mashId {-1} { + m_type {MashStep::Type::Infusion}, + m_infuseAmount_l {0.0 }, + m_stepTemp_c {0.0 }, + m_stepTime_min {0.0 }, + m_rampTime_min {0.0 }, + m_endTemp_c {0.0 }, + m_infuseTemp_c {0.0 }, + m_decoctionAmount_l{0.0 }, + m_stepNumber {0 }, + m_mashId {-1 } { return; } MashStep::MashStep(NamedParameterBundle const & namedParameterBundle) : - NamedEntity {namedParameterBundle}, - m_type {static_cast(namedParameterBundle(PropertyNames::MashStep::type).toInt())}, - m_infuseAmount_l {namedParameterBundle(PropertyNames::MashStep::infuseAmount_l ).toDouble()}, - m_stepTemp_c {namedParameterBundle(PropertyNames::MashStep::stepTemp_c ).toDouble()}, - m_stepTime_min {namedParameterBundle(PropertyNames::MashStep::stepTime_min ).toDouble()}, - m_rampTime_min {namedParameterBundle(PropertyNames::MashStep::rampTime_min ).toDouble()}, - m_endTemp_c {namedParameterBundle(PropertyNames::MashStep::endTemp_c ).toDouble()}, - m_infuseTemp_c {namedParameterBundle(PropertyNames::MashStep::infuseTemp_c ).toDouble()}, - m_decoctionAmount_l{namedParameterBundle(PropertyNames::MashStep::decoctionAmount_l).toDouble()}, - m_stepNumber {namedParameterBundle(PropertyNames::MashStep::stepNumber ).toInt()}, - mashId {namedParameterBundle(PropertyNames::MashStep::mashId ).toInt()} { + NamedEntity (namedParameterBundle ), + m_type (namedParameterBundle.val(PropertyNames::MashStep::type )), + m_infuseAmount_l (namedParameterBundle.val(PropertyNames::MashStep::infuseAmount_l )), + m_stepTemp_c (namedParameterBundle.val(PropertyNames::MashStep::stepTemp_c )), + m_stepTime_min (namedParameterBundle.val(PropertyNames::MashStep::stepTime_min )), + m_rampTime_min (namedParameterBundle.val(PropertyNames::MashStep::rampTime_min )), + m_endTemp_c (namedParameterBundle.val(PropertyNames::MashStep::endTemp_c )), + m_infuseTemp_c (namedParameterBundle.val(PropertyNames::MashStep::infuseTemp_c )), + m_decoctionAmount_l(namedParameterBundle.val(PropertyNames::MashStep::decoctionAmount_l)), + m_stepNumber (namedParameterBundle.val(PropertyNames::MashStep::stepNumber )), + m_mashId (namedParameterBundle.val(PropertyNames::MashStep::mashId )) { return; } @@ -96,104 +96,67 @@ MashStep::MashStep(MashStep const & other) : m_infuseTemp_c {other.m_infuseTemp_c }, m_decoctionAmount_l{other.m_decoctionAmount_l}, m_stepNumber {other.m_stepNumber }, - mashId {other.mashId } { + m_mashId {other.m_mashId } { return; } - MashStep::~MashStep() = default; //================================"SET" METHODS================================= -void MashStep::setInfuseTemp_c(double var) { - this->setAndNotify(PropertyNames::MashStep::infuseTemp_c, this->m_infuseTemp_c, var); -} - -void MashStep::setType(Type t) { - this->setAndNotify(PropertyNames::MashStep::type, this->m_type, t); -} - -void MashStep::setInfuseAmount_l(double var) { - this->setAndNotify(PropertyNames::MashStep::infuseAmount_l, this->m_infuseAmount_l, this->enforceMin(var, "infuse amount")); -} - -void MashStep::setStepTemp_c(double var) { - this->setAndNotify(PropertyNames::MashStep::stepTemp_c, this->m_stepTemp_c, this->enforceMin(var, "step temp", PhysicalConstants::absoluteZero)); -} - -void MashStep::setStepTime_min(double var) { - this->setAndNotify(PropertyNames::MashStep::stepTime_min, this->m_stepTime_min, this->enforceMin(var, "step time")); -} - -void MashStep::setRampTime_min(double var) { - this->setAndNotify(PropertyNames::MashStep::rampTime_min, this->m_rampTime_min, this->enforceMin(var, "ramp time")); -} - -void MashStep::setEndTemp_c(double var) { - this->setAndNotify(PropertyNames::MashStep::endTemp_c, this->m_endTemp_c, this->enforceMin(var, "end temp", PhysicalConstants::absoluteZero)); -} - -void MashStep::setDecoctionAmount_l(double var) { - this->setAndNotify(PropertyNames::MashStep::decoctionAmount_l, this->m_decoctionAmount_l, var); -} - - -void MashStep::setStepNumber(int stepNumber) { - this->setAndNotify(PropertyNames::MashStep::stepNumber, this->m_stepNumber, stepNumber); -} - -void MashStep::setMashId(int mashId) { - this->mashId = mashId; - this->propagatePropertyChange(PropertyNames::MashStep::mashId, false); - return; -} +void MashStep::setInfuseTemp_c (double val) { this->setAndNotify(PropertyNames::MashStep::infuseTemp_c , this->m_infuseTemp_c , val ); } +void MashStep::setType (Type val) { this->setAndNotify(PropertyNames::MashStep::type , this->m_type , val ); } +void MashStep::setInfuseAmount_l (double val) { this->setAndNotify(PropertyNames::MashStep::infuseAmount_l , this->m_infuseAmount_l , this->enforceMin(val, "infuse amount" )); } +void MashStep::setStepTemp_c (double val) { this->setAndNotify(PropertyNames::MashStep::stepTemp_c , this->m_stepTemp_c , this->enforceMin(val, "step temp", PhysicalConstants::absoluteZero)); } +void MashStep::setStepTime_min (double val) { this->setAndNotify(PropertyNames::MashStep::stepTime_min , this->m_stepTime_min , this->enforceMin(val, "step time" )); } +void MashStep::setRampTime_min (double val) { this->setAndNotify(PropertyNames::MashStep::rampTime_min , this->m_rampTime_min , this->enforceMin(val, "ramp time" )); } +void MashStep::setEndTemp_c (double val) { this->setAndNotify(PropertyNames::MashStep::endTemp_c , this->m_endTemp_c , this->enforceMin(val, "end temp" , PhysicalConstants::absoluteZero)); } +void MashStep::setDecoctionAmount_l(double val) { this->setAndNotify(PropertyNames::MashStep::decoctionAmount_l, this->m_decoctionAmount_l, val ); } +void MashStep::setStepNumber (int val) { this->setAndNotify(PropertyNames::MashStep::stepNumber , this->m_stepNumber , val ); } + +void MashStep::setMashId (int val) { this->m_mashId = val; this->propagatePropertyChange(PropertyNames::MashStep::mashId, false); return; } //============================="GET" METHODS==================================== MashStep::Type MashStep::type() const { return m_type; } -const QString MashStep::typeString() const { return types.at(m_type); } +const QString MashStep::typeString() const { return types.at(static_cast(this->m_type)); } const QString MashStep::typeStringTr() const { - if ( m_type < 0 || m_type > typesTr.length() ) { - return ""; - } - return typesTr.at(m_type); -} -double MashStep::infuseTemp_c() const { return m_infuseTemp_c; } -double MashStep::infuseAmount_l() const { return m_infuseAmount_l; } -double MashStep::stepTemp_c() const { return m_stepTemp_c; } -double MashStep::stepTime_min() const { return m_stepTime_min; } -double MashStep::rampTime_min() const { return m_rampTime_min; } -double MashStep::endTemp_c() const { return m_endTemp_c; } -double MashStep::decoctionAmount_l() const { return m_decoctionAmount_l; } -int MashStep::stepNumber() const { return m_stepNumber; } -//Mash * MashStep::mash( ) const { return m_mash; } -int MashStep::getMashId() const { return this->mashId; } - -bool MashStep::isInfusion() const -{ - return ( m_type == MashStep::Infusion || - m_type == MashStep::batchSparge || - m_type == MashStep::flySparge ); -} - -bool MashStep::isSparge() const -{ - return ( m_type == MashStep::batchSparge || - m_type == MashStep::flySparge || + int myType = static_cast(this->m_type); + Q_ASSERT(myType >= 0); + Q_ASSERT(myType < typesTr.length()); + return typesTr.at(myType); +} +double MashStep::infuseTemp_c () const { return this->m_infuseTemp_c ; } +double MashStep::infuseAmount_l () const { return this->m_infuseAmount_l ; } +double MashStep::stepTemp_c () const { return this->m_stepTemp_c ; } +double MashStep::stepTime_min () const { return this->m_stepTime_min ; } +double MashStep::rampTime_min () const { return this->m_rampTime_min ; } +double MashStep::endTemp_c () const { return this->m_endTemp_c ; } +double MashStep::decoctionAmount_l() const { return this->m_decoctionAmount_l; } +int MashStep::stepNumber () const { return this->m_stepNumber ; } +int MashStep::getMashId () const { return this->m_mashId ; } + +bool MashStep::isInfusion() const { + return ( m_type == MashStep::Type::Infusion || + m_type == MashStep::Type::batchSparge || + m_type == MashStep::Type::flySparge ); +} + +bool MashStep::isSparge() const { + return ( m_type == MashStep::Type::batchSparge || + m_type == MashStep::Type::flySparge || name() == "Final Batch Sparge" ); } -bool MashStep::isTemperature() const -{ - return ( m_type == MashStep::Temperature ); +bool MashStep::isTemperature() const { + return ( m_type == MashStep::Type::Temperature ); } -bool MashStep::isDecoction() const -{ - return ( m_type == MashStep::Decoction ); +bool MashStep::isDecoction() const { + return ( m_type == MashStep::Type::Decoction ); } Recipe * MashStep::getOwningRecipe() { - Mash * mash = ObjectStoreWrapper::getByIdRaw(this->mashId); + Mash * mash = ObjectStoreWrapper::getByIdRaw(this->m_mashId); if (!mash) { return nullptr; } diff --git a/src/model/MashStep.h b/src/model/MashStep.h index 5d446a256..3fd2fe54a 100644 --- a/src/model/MashStep.h +++ b/src/model/MashStep.h @@ -1,6 +1,6 @@ /* * model/MashStep.h is part of Brewtarget, and is Copyright the following - * authors 2009-2021 + * authors 2009-2023 * - Jeff Bailey * - Matt Young * - Philip Greggory Lee @@ -66,7 +66,7 @@ class MashStep : public NamedEntity { //! \brief The type of step. enum Type { Infusion, Temperature, Decoction, flySparge, batchSparge }; - Q_ENUMS( Type ) + Q_ENUM(Type) MashStep(QString name = ""); MashStep(NamedParameterBundle const & namedParameterBundle); @@ -150,7 +150,7 @@ class MashStep : public NamedEntity { double m_infuseTemp_c; double m_decoctionAmount_l; int m_stepNumber; - int mashId; + int m_mashId; }; #endif diff --git a/src/model/Misc.cpp b/src/model/Misc.cpp index a89f5eac2..404863251 100644 --- a/src/model/Misc.cpp +++ b/src/model/Misc.cpp @@ -59,6 +59,31 @@ ObjectStore & Misc::getObjectStoreTypedInstance() const { } //============================CONSTRUCTORS====================================== + +Misc::Misc(QString name) : + NamedEntityWithInventory{name, true}, + m_type {Misc::Type::Spice}, + m_use {Misc::Use::Boil }, + m_time {0.0 }, + m_amount {0.0 }, + m_amountIsWeight{false }, + m_useFor {"" }, + m_notes {"" } { + return; +} + +Misc::Misc(NamedParameterBundle const & namedParameterBundle) : + NamedEntityWithInventory{namedParameterBundle}, + m_type {namedParameterBundle.val(PropertyNames::Misc::type )}, + m_use {namedParameterBundle.val(PropertyNames::Misc::use )}, + m_time {namedParameterBundle.val(PropertyNames::Misc::time )}, + m_amount {namedParameterBundle.val(PropertyNames::Misc::amount )}, + m_amountIsWeight {namedParameterBundle.val(PropertyNames::Misc::amountIsWeight)}, + m_useFor {namedParameterBundle.val(PropertyNames::Misc::useFor )}, + m_notes {namedParameterBundle.val(PropertyNames::Misc::notes )} { + return; +} + Misc::Misc(Misc const & other) : NamedEntityWithInventory{other }, m_type {other.m_type }, @@ -71,30 +96,7 @@ Misc::Misc(Misc const & other) : return; } -Misc::Misc(QString name) : - NamedEntityWithInventory{name, true}, - m_type {Misc::Type::Spice}, - m_use {Misc::Use::Boil }, - m_time {0.0 }, - m_amount {0.0 }, - m_amountIsWeight {false }, - m_useFor {"" }, - m_notes {"" } { - return; -} - -Misc::Misc(NamedParameterBundle const & namedParameterBundle) : - NamedEntityWithInventory{namedParameterBundle}, - m_type {static_cast(namedParameterBundle(PropertyNames::Misc::type).toInt())}, - m_use {static_cast(namedParameterBundle(PropertyNames::Misc::use).toInt())}, - m_time {namedParameterBundle(PropertyNames::Misc::time ).toDouble()}, - m_amount {namedParameterBundle(PropertyNames::Misc::amount ).toDouble()}, - m_amountIsWeight {namedParameterBundle(PropertyNames::Misc::amountIsWeight).toBool()}, - m_useFor {namedParameterBundle(PropertyNames::Misc::useFor ).toString()}, - m_notes {namedParameterBundle(PropertyNames::Misc::notes ).toString()} { - return; -} - +Misc::~Misc() = default; //============================"GET" METHODS===================================== Misc::Type Misc::type() const { return m_type; } diff --git a/src/model/Misc.h b/src/model/Misc.h index a1c44e913..a5498c7dc 100644 --- a/src/model/Misc.h +++ b/src/model/Misc.h @@ -1,6 +1,6 @@ /* * model/Misc.h is part of Brewtarget, and is Copyright the following - * authors 2009-2021 + * authors 2009-2023 * - Jeff Bailey * - Matt Young * - Mik Firestone @@ -64,13 +64,15 @@ class Misc : public NamedEntityWithInventory { enum class Use { Boil, Mash, Primary, Secondary, Bottling }; //! \brief What is the type of amount. enum class AmountType { Weight, Volume }; - Q_ENUMS( Type Use AmountType ) + Q_ENUM(Type) + Q_ENUM(Use) + Q_ENUM(AmountType) Misc(QString name = ""); Misc(NamedParameterBundle const & namedParameterBundle); Misc(Misc const & other); - virtual ~Misc() = default; + virtual ~Misc(); //! \brief The \c Type. Q_PROPERTY( Type type READ type WRITE setType /*NOTIFY changed*/ /*changedType*/ ) diff --git a/src/model/NamedEntity.cpp b/src/model/NamedEntity.cpp index 852fc3de7..96b348b92 100644 --- a/src/model/NamedEntity.cpp +++ b/src/model/NamedEntity.cpp @@ -32,26 +32,14 @@ #include "model/Recipe.h" NamedEntity::NamedEntity(QString t_name, bool t_display, QString folder) : - QObject {nullptr }, - m_key {-1 }, - parentKey {-1 }, - m_folder {folder }, - m_name {t_name }, - m_display {t_display}, - m_deleted {false }, - m_beingModified{false} { - return; -} - -NamedEntity::NamedEntity(NamedEntity const & other) : - QObject {nullptr }, // QObject doesn't have a copy constructor, so just make a new one - m_key {-1 }, // We don't want to copy the other object's key/ID - parentKey {other.parentKey }, - m_folder {other.m_folder }, - m_name {other.m_name }, - m_display {other.m_display }, - m_deleted {other.m_deleted }, - m_beingModified{false} { + QObject {nullptr }, + m_key {-1 }, + parentKey {-1 }, + m_folder {folder }, + m_name {t_name }, + m_display {t_display}, + m_deleted {false }, + m_beingModified{false } { return; } @@ -70,13 +58,25 @@ NamedEntity::NamedEntity(NamedEntity const & other) : // future. // NamedEntity::NamedEntity(NamedParameterBundle const & namedParameterBundle) : - QObject{nullptr}, - m_key {namedParameterBundle(PropertyNames::NamedEntity::key, -1) }, - parentKey {namedParameterBundle(PropertyNames::NamedEntity::parentKey, -1) }, - m_folder {namedParameterBundle(PropertyNames::NamedEntity::folder, QString{})}, - m_name {namedParameterBundle(PropertyNames::NamedEntity::name, QString{}) }, - m_display {namedParameterBundle(PropertyNames::NamedEntity::display, true) }, - m_deleted {namedParameterBundle(PropertyNames::NamedEntity::deleted, false) }, + QObject {nullptr}, + m_key {namedParameterBundle.val(PropertyNames::NamedEntity::key, -1 )}, + parentKey {namedParameterBundle.val(PropertyNames::NamedEntity::parentKey, -1 )}, + m_folder {namedParameterBundle.val(PropertyNames::NamedEntity::folder, QString{})}, + m_name {namedParameterBundle.val(PropertyNames::NamedEntity::name, QString{})}, + m_display {namedParameterBundle.val(PropertyNames::NamedEntity::display, true )}, + m_deleted {namedParameterBundle.val(PropertyNames::NamedEntity::deleted, false )}, + m_beingModified{false} { + return; +} + +NamedEntity::NamedEntity(NamedEntity const & other) : + QObject {nullptr }, // QObject doesn't have a copy constructor, so just make a new one + m_key {-1 }, // We don't want to copy the other object's key/ID + parentKey {other.parentKey}, + m_folder {other.m_folder }, + m_name {other.m_name }, + m_display {other.m_display}, + m_deleted {other.m_deleted}, m_beingModified{false} { return; } diff --git a/src/model/NamedEntityWithInventory.cpp b/src/model/NamedEntityWithInventory.cpp index cd580c420..41f62da35 100644 --- a/src/model/NamedEntityWithInventory.cpp +++ b/src/model/NamedEntityWithInventory.cpp @@ -34,7 +34,7 @@ NamedEntityWithInventory::NamedEntityWithInventory(QString t_name, NamedEntityWithInventory::NamedEntityWithInventory(NamedParameterBundle const & namedParameterBundle) : NamedEntity {namedParameterBundle}, // If we're reading in from a BeerXML file, there won't be an inventory ID - m_inventory_id{namedParameterBundle(PropertyNames::NamedEntityWithInventory::inventoryId, -1)} { + m_inventory_id{namedParameterBundle.val(PropertyNames::NamedEntityWithInventory::inventoryId, -1)} { return; } @@ -46,6 +46,8 @@ NamedEntityWithInventory::NamedEntityWithInventory(NamedEntityWithInventory cons return; } +NamedEntityWithInventory::~NamedEntityWithInventory() = default; + void NamedEntityWithInventory::makeChild(NamedEntity const & copiedFrom) { // First do the base class work this->NamedEntity::makeChild(copiedFrom); diff --git a/src/model/NamedEntityWithInventory.h b/src/model/NamedEntityWithInventory.h index ec3953a47..e3286735c 100644 --- a/src/model/NamedEntityWithInventory.h +++ b/src/model/NamedEntityWithInventory.h @@ -1,6 +1,6 @@ /* * model/NamedEntityWithInventory.h is part of Brewtarget, and is Copyright the following - * authors 2021 + * authors 2021-2023 * - Matt Young * * Brewtarget is free software: you can redistribute it and/or modify @@ -44,7 +44,7 @@ class NamedEntityWithInventory : public NamedEntity { NamedEntityWithInventory(NamedEntityWithInventory const & other); NamedEntityWithInventory(NamedParameterBundle const & namedParameterBundle); - virtual ~NamedEntityWithInventory() = default; + virtual ~NamedEntityWithInventory(); //! \brief The amount in inventory (usually in kg) Q_PROPERTY( double inventory READ inventory WRITE setInventoryAmount /*NOTIFY changed*/ /*changedInventory*/ ) diff --git a/src/model/NamedParameterBundle.cpp b/src/model/NamedParameterBundle.cpp index ba2994e13..30273b450 100644 --- a/src/model/NamedParameterBundle.cpp +++ b/src/model/NamedParameterBundle.cpp @@ -28,14 +28,6 @@ #include #include -namespace { - template T valueFromQVariant(QVariant const & qv); - template <> QString valueFromQVariant(QVariant const & qv) {return qv.toString();} - template <> bool valueFromQVariant(QVariant const & qv) {return qv.toBool();} - template <> int valueFromQVariant(QVariant const & qv) {return qv.toInt();} - template <> double valueFromQVariant(QVariant const & qv) {return qv.toDouble();} -} - NamedParameterBundle::NamedParameterBundle(NamedParameterBundle::OperationMode mode) : QHash(), mode{mode} { @@ -50,7 +42,7 @@ NamedParameterBundle::iterator NamedParameterBundle::insert(BtStringConst const } -QVariant NamedParameterBundle::operator()(BtStringConst const & parameterName) const { +QVariant NamedParameterBundle::get(BtStringConst const & parameterName) const { if (!this->contains(*parameterName)) { QString errorMessage = QString("No value supplied for required parameter, %1.").arg(*parameterName); QTextStream errorMessageAsStream(&errorMessage); @@ -98,22 +90,6 @@ QVariant NamedParameterBundle::operator()(BtStringConst const & parameterName) c return returnValue; } -template T NamedParameterBundle::operator()(BtStringConst const & parameterName, - T const & defaultValue) const { - Q_ASSERT(!parameterName.isNull()); - return this->contains(*parameterName) ? valueFromQVariant(this->value(*parameterName)) : defaultValue; -} - -// -// 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.) -// -template QString NamedParameterBundle::operator()(BtStringConst const & parameterName, QString const & defaultValue) const; -template bool NamedParameterBundle::operator()(BtStringConst const & parameterName, bool const & defaultValue) const; -template int NamedParameterBundle::operator()(BtStringConst const & parameterName, int const & defaultValue) const; -template double NamedParameterBundle::operator()(BtStringConst const & parameterName, double const & defaultValue) const; - - template S & operator<<(S & stream, NamedParameterBundle const & namedParameterBundle); diff --git a/src/model/NamedParameterBundle.h b/src/model/NamedParameterBundle.h index 9aed19e4a..88c9000b8 100644 --- a/src/model/NamedParameterBundle.h +++ b/src/model/NamedParameterBundle.h @@ -20,12 +20,50 @@ #define MODEL_NAMEDPARAMETERBUNDLE_H #pragma once +#include + +#include #include #include #include +#include "measurement/Amount.h" #include "utils/BtStringConst.h" +// +// It is useful in various places to be able to store member variables in QVariant objects. +// +// Where we define a strongly-typed enum, we usually just need a corresponding Q_ENUM declaration in the same header. +// This works with generic serialisation code (eg to and from database or BeerJSON) because you can safely static_cast +// between the strongly typed enum and an integer, so the generic code can use integers (via EnumStringMapping) and the +// class-specific code can use the strongly-typed enums and everything just work. +// +// HOWEVER, when the enum is optional (ie stored in memory inside std::optional, stored in the DB as a nullable field, +// only an optional field in BeerJSON, etc) then we cannot rely on casting. You cannot, eg, static_cast between +// std::optional and std::optional. So inside NamedParameterBundle, we always store +// std::optional for optional enum fields inside QVariant. We need this Q_DECLARE_METATYPE macro here to allow +// this to happen. +// +// We then put template wrappers in NamedParameterBundle so things aren't too clunky in the class-specific code. +// +// Similarly, for other nullable fields, we need to declare that we want to store std::optional inside +// QVariant. This is a convenient place to do it because this header gets included not only by all the model classes +// but also by all the different serialisation code (Database, XML, JSON). +// +// Note that Qt MOC will error if you repeat a Q_DECLARE_METATYPE() declaration for the same type, which is another +// reason to put them all in one central place rather than try to declare as needed individually. +// +Q_DECLARE_METATYPE(std::optional) +Q_DECLARE_METATYPE(std::optional) +Q_DECLARE_METATYPE(std::optional) +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 +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 * those parameters to be data-driven, eg from a mapping of database column names to property names. @@ -50,20 +88,65 @@ class NamedParameterBundle : public QHash { * exception if it is not present. Otherwise, return whatever default value QVariant gives us. * This is a convenience function to make the call to extract parameters concise. (We don't want to use the * operator[] of QHash because we want "parameter not found" to be an error.) + * + * \throw std::invalid_argument if the parameter is not present or does not have a valid \c QVariant value */ - QVariant operator()(BtStringConst const & parameterName) const; + QVariant get(BtStringConst const & parameterName) const; /** - * \brief Get the value of a parameter that is not required to be present + * \brief Templated version of above * - * (NB: There is no general implementation of this templated function, just specific specialisations) + * \throw std::invalid_argument if the parameter is not present or does not have a valid \c QVariant value + */ + template T val(BtStringConst const & parameterName) const { + return this->get(parameterName).value(); + } + + /** + * \brief Special case for optional enums which are always stored as std::optional inside the QVariant. + * Obviously by definition there's always a default value and it's always std::nullopt + * + * \throw std::invalid_argument if the parameter is not present or does not have a valid \c QVariant value + */ + template std::optional optEnumVal(BtStringConst const & parameterName) const { + // Of course it's a coding error to request a parameter without a name! + Q_ASSERT(!parameterName.isNull()); + if (!this->contains(*parameterName)) { + return std::nullopt; + } + auto value = this->value(*parameterName).value< std::optional >(); + if (value.has_value()) { + return std::optional(static_cast(value.value())); + } + return std::nullopt; + } + + /** + * \brief Get the value of a parameter that is not required to be present * * \param parameterName * \param defaultValue What to return if the parameter is not present in the bundle */ - template T operator()(BtStringConst const & parameterName, T const & defaultValue) const; + template T val(BtStringConst const & parameterName, T const & defaultValue) const { + // Of course it's a coding error to request a parameter without a name! + Q_ASSERT(!parameterName.isNull()); + // In expression below, first value() is QHash::value(), second is templated QVariant::value() + return this->contains(*parameterName) ? this->value(*parameterName).value() : defaultValue; + } private: OperationMode mode; }; +/** + * \brief Convenience function to allow output of \c NamedParameterBundle to \c QDebug or \c QTextStream stream etc + */ +template +S & operator<<(S & stream, NamedParameterBundle const & namedParameterBundle); + +/** + * \brief Convenience function to allow output of \c NamedParameterBundle to \c QDebug or \c QTextStream stream etc + */ +template +S & operator<<(S & stream, NamedParameterBundle const * namedParameterBundle); + #endif diff --git a/src/model/Recipe.cpp b/src/model/Recipe.cpp index 0a7715352..761f92352 100644 --- a/src/model/Recipe.cpp +++ b/src/model/Recipe.cpp @@ -1,6 +1,6 @@ /* * model/Recipe.cpp is part of Brewtarget, and is Copyright the following - * authors 2009-2022 + * authors 2009-2023 * - Kregg K * - Matt Young * - Mik Firestone @@ -401,46 +401,46 @@ Recipe::Recipe(QString name) : } Recipe::Recipe(NamedParameterBundle const & namedParameterBundle) : - NamedEntity{namedParameterBundle}, - pimpl{std::make_unique(*this)}, + NamedEntity {namedParameterBundle }, + pimpl {std::make_unique(*this)}, m_type { // .:TODO:. Change so we store enum not string! - RECIPE_TYPE_STRING_TO_TYPE.key(static_cast(namedParameterBundle(PropertyNames::Recipe::recipeType).toInt())) + RECIPE_TYPE_STRING_TO_TYPE.key(namedParameterBundle.val(PropertyNames::Recipe::recipeType)) }, - m_brewer {namedParameterBundle(PropertyNames::Recipe::brewer).toString() }, - m_asstBrewer {namedParameterBundle(PropertyNames::Recipe::asstBrewer).toString() }, - m_batchSize_l {namedParameterBundle(PropertyNames::Recipe::batchSize_l).toDouble() }, - m_boilSize_l {namedParameterBundle(PropertyNames::Recipe::boilSize_l).toDouble() }, - m_boilTime_min {namedParameterBundle(PropertyNames::Recipe::boilTime_min).toDouble() }, - m_efficiency_pct {namedParameterBundle(PropertyNames::Recipe::efficiency_pct).toDouble() }, - m_fermentationStages{namedParameterBundle(PropertyNames::Recipe::fermentationStages).toInt() }, - m_primaryAge_days {namedParameterBundle(PropertyNames::Recipe::primaryAge_days).toDouble() }, - m_primaryTemp_c {namedParameterBundle(PropertyNames::Recipe::primaryTemp_c).toDouble() }, - m_secondaryAge_days {namedParameterBundle(PropertyNames::Recipe::secondaryAge_days).toDouble()}, - m_secondaryTemp_c {namedParameterBundle(PropertyNames::Recipe::secondaryTemp_c).toDouble() }, - m_tertiaryAge_days {namedParameterBundle(PropertyNames::Recipe::tertiaryAge_days).toDouble() }, - m_tertiaryTemp_c {namedParameterBundle(PropertyNames::Recipe::tertiaryTemp_c).toDouble() }, - m_age {namedParameterBundle(PropertyNames::Recipe::age).toDouble() }, - m_ageTemp_c {namedParameterBundle(PropertyNames::Recipe::ageTemp_c).toDouble() }, - m_date {namedParameterBundle(PropertyNames::Recipe::date).toDate() }, - m_carbonation_vols {namedParameterBundle(PropertyNames::Recipe::carbonation_vols).toDouble() }, - m_forcedCarbonation {namedParameterBundle(PropertyNames::Recipe::forcedCarbonation).toBool() }, - m_primingSugarName {namedParameterBundle(PropertyNames::Recipe::primingSugarName).toString() }, - m_carbonationTemp_c {namedParameterBundle(PropertyNames::Recipe::carbonationTemp_c).toDouble()}, - m_primingSugarEquiv {namedParameterBundle(PropertyNames::Recipe::primingSugarEquiv).toDouble()}, - m_kegPrimingFactor {namedParameterBundle(PropertyNames::Recipe::kegPrimingFactor).toDouble() }, - m_notes {namedParameterBundle(PropertyNames::Recipe::notes).toString() }, - m_tasteNotes {namedParameterBundle(PropertyNames::Recipe::tasteNotes).toString() }, - m_tasteRating {namedParameterBundle(PropertyNames::Recipe::tasteRating).toDouble() }, - styleId {namedParameterBundle(PropertyNames::Recipe::styleId).toInt() }, - mashId {namedParameterBundle(PropertyNames::Recipe::mashId).toInt() }, - equipmentId {namedParameterBundle(PropertyNames::Recipe::equipmentId).toInt() }, - m_og {namedParameterBundle(PropertyNames::Recipe::og).toDouble() }, - m_fg {namedParameterBundle(PropertyNames::Recipe::fg).toDouble() }, - m_locked {namedParameterBundle(PropertyNames::Recipe::locked).toBool() }, - m_ancestor_id {namedParameterBundle(PropertyNames::Recipe::ancestorId).toInt() }, + m_brewer {namedParameterBundle.val(PropertyNames::Recipe::brewer )}, + m_asstBrewer {namedParameterBundle.val(PropertyNames::Recipe::asstBrewer )}, + m_batchSize_l {namedParameterBundle.val(PropertyNames::Recipe::batchSize_l )}, + m_boilSize_l {namedParameterBundle.val(PropertyNames::Recipe::boilSize_l )}, + m_boilTime_min {namedParameterBundle.val(PropertyNames::Recipe::boilTime_min )}, + m_efficiency_pct {namedParameterBundle.val(PropertyNames::Recipe::efficiency_pct )}, + m_fermentationStages{namedParameterBundle.val(PropertyNames::Recipe::fermentationStages)}, + m_primaryAge_days {namedParameterBundle.val(PropertyNames::Recipe::primaryAge_days )}, + m_primaryTemp_c {namedParameterBundle.val(PropertyNames::Recipe::primaryTemp_c )}, + m_secondaryAge_days {namedParameterBundle.val(PropertyNames::Recipe::secondaryAge_days )}, + m_secondaryTemp_c {namedParameterBundle.val(PropertyNames::Recipe::secondaryTemp_c )}, + m_tertiaryAge_days {namedParameterBundle.val(PropertyNames::Recipe::tertiaryAge_days )}, + m_tertiaryTemp_c {namedParameterBundle.val(PropertyNames::Recipe::tertiaryTemp_c )}, + m_age {namedParameterBundle.val(PropertyNames::Recipe::age )}, + m_ageTemp_c {namedParameterBundle.val(PropertyNames::Recipe::ageTemp_c )}, + m_date {namedParameterBundle.val(PropertyNames::Recipe::date )}, + m_carbonation_vols {namedParameterBundle.val(PropertyNames::Recipe::carbonation_vols )}, + m_forcedCarbonation {namedParameterBundle.val(PropertyNames::Recipe::forcedCarbonation )}, + m_primingSugarName {namedParameterBundle.val(PropertyNames::Recipe::primingSugarName )}, + m_carbonationTemp_c {namedParameterBundle.val(PropertyNames::Recipe::carbonationTemp_c )}, + m_primingSugarEquiv {namedParameterBundle.val(PropertyNames::Recipe::primingSugarEquiv )}, + m_kegPrimingFactor {namedParameterBundle.val(PropertyNames::Recipe::kegPrimingFactor )}, + m_notes {namedParameterBundle.val(PropertyNames::Recipe::notes )}, + m_tasteNotes {namedParameterBundle.val(PropertyNames::Recipe::tasteNotes )}, + m_tasteRating {namedParameterBundle.val(PropertyNames::Recipe::tasteRating )}, + styleId {namedParameterBundle.val(PropertyNames::Recipe::styleId )}, + mashId {namedParameterBundle.val(PropertyNames::Recipe::mashId )}, + equipmentId {namedParameterBundle.val(PropertyNames::Recipe::equipmentId )}, + m_og {namedParameterBundle.val(PropertyNames::Recipe::og )}, + m_fg {namedParameterBundle.val(PropertyNames::Recipe::fg )}, + m_locked {namedParameterBundle.val(PropertyNames::Recipe::locked )}, + m_ancestor_id {namedParameterBundle.val(PropertyNames::Recipe::ancestorId )}, m_ancestors {}, - m_hasDescendants {false } { + m_hasDescendants {false} { // At this stage, we haven't set any Hops, Fermentables, etc. This is deliberate because the caller typically needs // to access subsidiary records to obtain this info. Callers will usually use setters (setHopIds, etc but via // setProperty) to finish constructing the object. @@ -861,14 +861,10 @@ bool Recipe::hasBoilExtract() { } PreInstruction Recipe::boilFermentablesPre(double timeRemaining) { - QString str; - int i; - int size; - - str = tr("Boil or steep "); + QString str = tr("Boil or steep "); QList flist = fermentables(); - size = flist.size(); - for (i = 0; static_cast(i) < size; ++i) { + int size = flist.size(); + for (int i = 0; static_cast(i) < size; ++i) { Fermentable * ferm = flist[i]; if (ferm->isMashed() || ferm->addAfterBoil() || ferm->isExtract()) { continue; @@ -894,14 +890,10 @@ bool Recipe::isFermentableSugar(Fermentable * fermy) { } PreInstruction Recipe::addExtracts(double timeRemaining) const { - QString str; - int i; - int size; - - str = tr("Raise water to boil and then remove from heat. Stir in "); + QString str = tr("Raise water to boil and then remove from heat. Stir in "); const QList flist = fermentables(); - size = flist.size(); - for (i = 0; static_cast(i) < size; ++i) { + int size = flist.size(); + for (int i = 0; static_cast(i) < size; ++i) { const Fermentable * ferm = flist[i]; if (ferm->isExtract()) { str += QString("%1 %2, ") @@ -1066,7 +1058,7 @@ void Recipe::generateInstructions() { Measurement::qStringToSI(QInputDialog::getText(nullptr, tr("Boil time"), tr("You did not configure an equipment (which you really should), so tell me the boil time.")), - Measurement::PhysicalQuantity::Time).quantity; + Measurement::PhysicalQuantity::Time).quantity(); } QString str = tr("Bring the wort to a boil and hold for %1.").arg( @@ -1993,51 +1985,51 @@ template QList< std::shared_ptr > Recipe::getAll() const; template QList< std::shared_ptr > Recipe::getAll() const; template QList< std::shared_ptr > Recipe::getAll() const; -QList Recipe::hops() const { return this->pimpl->getAllMyRaw(); } -QVector Recipe::getHopIds() const { return this->pimpl->hopIds; } -QList Recipe::fermentables() const { return this->pimpl->getAllMyRaw(); } -QVector Recipe::getFermentableIds() const { return this->pimpl->fermentableIds; } -QList Recipe::miscs() const { return this->pimpl->getAllMyRaw(); } -QVector Recipe::getMiscIds() const { return this->pimpl->miscIds; } -QList Recipe::yeasts() const { return this->pimpl->getAllMyRaw(); } -QVector Recipe::getYeastIds() const { return this->pimpl->yeastIds; } -QList Recipe::waters() const { return this->pimpl->getAllMyRaw(); } -QVector Recipe::getWaterIds() const { return this->pimpl->waterIds; } -QList Recipe::salts() const { return this->pimpl->getAllMyRaw(); } -QVector Recipe::getSaltIds() const { return this->pimpl->saltIds; } -int Recipe::getAncestorId() const { return this->m_ancestor_id; } +QList Recipe::hops() const { return this->pimpl->getAllMyRaw(); } +QVector Recipe::getHopIds() const { return this->pimpl->hopIds; } +QList Recipe::fermentables() const { return this->pimpl->getAllMyRaw(); } +QVector Recipe::getFermentableIds() const { return this->pimpl->fermentableIds; } +QList Recipe::miscs() const { return this->pimpl->getAllMyRaw(); } +QVector Recipe::getMiscIds() const { return this->pimpl->miscIds; } +QList Recipe::yeasts() const { return this->pimpl->getAllMyRaw(); } +QVector Recipe::getYeastIds() const { return this->pimpl->yeastIds; } +QList Recipe::waters() const { return this->pimpl->getAllMyRaw(); } +QVector Recipe::getWaterIds() const { return this->pimpl->waterIds; } +QList Recipe::salts() const { return this->pimpl->getAllMyRaw(); } +QVector Recipe::getSaltIds() const { return this->pimpl->saltIds; } +int Recipe::getAncestorId() const { return this->m_ancestor_id; } //==============================Getters=================================== Recipe::Type Recipe::recipeType() const { return RECIPE_TYPE_STRING_TO_TYPE.value(this->type()); } QString Recipe::type() const { return m_type; } -QString Recipe::brewer() const { return m_brewer; } -QString Recipe::asstBrewer() const { return m_asstBrewer; } -QString Recipe::notes() const { return m_notes; } -QString Recipe::tasteNotes() const { return m_tasteNotes; } -QString Recipe::primingSugarName() const { return m_primingSugarName; } -bool Recipe::forcedCarbonation() const { return m_forcedCarbonation; } -double Recipe::batchSize_l() const { return m_batchSize_l; } -double Recipe::boilSize_l() const { return m_boilSize_l; } -double Recipe::boilTime_min() const { return m_boilTime_min; } -double Recipe::efficiency_pct() const { return m_efficiency_pct; } -double Recipe::tasteRating() const { return m_tasteRating; } -double Recipe::primaryAge_days() const { return m_primaryAge_days; } -double Recipe::primaryTemp_c() const { return m_primaryTemp_c; } -double Recipe::secondaryAge_days() const { return m_secondaryAge_days; } -double Recipe::secondaryTemp_c() const { return m_secondaryTemp_c; } -double Recipe::tertiaryAge_days() const { return m_tertiaryAge_days; } -double Recipe::tertiaryTemp_c() const { return m_tertiaryTemp_c; } -double Recipe::age_days() const { return m_age; } -double Recipe::ageTemp_c() const { return m_ageTemp_c; } -double Recipe::carbonation_vols() const { return m_carbonation_vols; } -double Recipe::carbonationTemp_c() const { return m_carbonationTemp_c; } -double Recipe::primingSugarEquiv() const { return m_primingSugarEquiv; } -double Recipe::kegPrimingFactor() const { return m_kegPrimingFactor; } +QString Recipe::brewer() const { return m_brewer; } +QString Recipe::asstBrewer() const { return m_asstBrewer; } +QString Recipe::notes() const { return m_notes; } +QString Recipe::tasteNotes() const { return m_tasteNotes; } +QString Recipe::primingSugarName() const { return m_primingSugarName; } +bool Recipe::forcedCarbonation() const { return m_forcedCarbonation; } +double Recipe::batchSize_l() const { return m_batchSize_l; } +double Recipe::boilSize_l() const { return m_boilSize_l; } +double Recipe::boilTime_min() const { return m_boilTime_min; } +double Recipe::efficiency_pct() const { return m_efficiency_pct; } +double Recipe::tasteRating() const { return m_tasteRating; } +double Recipe::primaryAge_days() const { return m_primaryAge_days; } +double Recipe::primaryTemp_c() const { return m_primaryTemp_c; } +double Recipe::secondaryAge_days() const { return m_secondaryAge_days; } +double Recipe::secondaryTemp_c() const { return m_secondaryTemp_c; } +double Recipe::tertiaryAge_days() const { return m_tertiaryAge_days; } +double Recipe::tertiaryTemp_c() const { return m_tertiaryTemp_c; } +double Recipe::age_days() const { return m_age; } +double Recipe::ageTemp_c() const { return m_ageTemp_c; } +double Recipe::carbonation_vols() const { return m_carbonation_vols; } +double Recipe::carbonationTemp_c() const { return m_carbonationTemp_c; } +double Recipe::primingSugarEquiv() const { return m_primingSugarEquiv; } +double Recipe::kegPrimingFactor() const { return m_kegPrimingFactor; } int Recipe::fermentationStages() const { return m_fermentationStages; } -QDate Recipe::date() const { return m_date; } -bool Recipe::locked() const { return m_locked; } +QDate Recipe::date() const { return m_date; } +bool Recipe::locked() const { return m_locked; } //=============================Adders and Removers======================================== @@ -2134,7 +2126,7 @@ void Recipe::recalcColor_srm() { QList ferms = fermentables(); for (i = 0; static_cast(i) < ferms.size(); ++i) { ferm = ferms[i]; - // Conversion factor for lb/gal to kg/l = 8.34538. + // Conversion factor for lb/gal to kg/l = 8.34538. mcu += ferm->color_srm() * 8.34538 * ferm->amount_kg() / m_finalVolumeNoLosses_l; } @@ -2147,6 +2139,7 @@ void Recipe::recalcColor_srm() { } } + return; } void Recipe::recalcIBU() { @@ -2166,7 +2159,7 @@ void Recipe::recalcIBU() { // Bitterness due to hopped extracts... QList ferms = fermentables(); for (i = 0; static_cast(i) < ferms.size(); ++i) { - // Conversion factor for lb/gal to kg/l = 8.34538. + // Conversion factor for lb/gal to kg/l = 8.34538. ibus += ferms[i]->ibuGalPerLb() * (ferms[i]->amount_kg() / batchSize_l()) / 8.34538; @@ -2178,6 +2171,8 @@ void Recipe::recalcIBU() { emit changed(metaProperty(*PropertyNames::Recipe::IBU), m_IBU); } } + + return; } void Recipe::recalcVolumeEstimates() { @@ -2278,6 +2273,7 @@ void Recipe::recalcVolumeEstimates() { emit changed(metaProperty(*PropertyNames::Recipe::postBoilVolume_l), m_postBoilVolume_l); } } + return; } void Recipe::recalcGrainsInMash_kg() { @@ -2301,6 +2297,7 @@ void Recipe::recalcGrainsInMash_kg() { emit changed(metaProperty(*PropertyNames::Recipe::grainsInMash_kg), m_grainsInMash_kg); } } + return; } void Recipe::recalcGrains_kg() { @@ -2819,7 +2816,6 @@ double Recipe::targetTotalMashVol_l() { absorption_lKg = PhysicalConstants::grainAbsorption_Lkg; } - return targetCollectedWortVol_l() + absorption_lKg * grainsInMash_kg(); } diff --git a/src/model/Recipe.h b/src/model/Recipe.h index 1a6938b4c..1a5b1a27d 100644 --- a/src/model/Recipe.h +++ b/src/model/Recipe.h @@ -1,6 +1,6 @@ /* * model/Recipe.h is part of Brewtarget, and is Copyright the following - * authors 2009-2022 + * authors 2009-2023 * - Jeff Bailey * - Kregg K * - Matt Young @@ -152,7 +152,7 @@ class Recipe : public NamedEntity { //! \brief The type of recipe enum class Type { Extract, PartialMash, AllGrain }; - Q_ENUMS(Type) + Q_ENUM(Type) //! \brief The \b Type Q_PROPERTY(Type recipeType READ recipeType WRITE setRecipeType /*NOTIFY changed*/ /*changedType*/) diff --git a/src/model/Salt.cpp b/src/model/Salt.cpp index ee42a5c6c..a0651a52f 100644 --- a/src/model/Salt.cpp +++ b/src/model/Salt.cpp @@ -1,6 +1,6 @@ /* * model/Salt.cpp is part of Brewtarget, and is Copyright the following - * authors 2009-2021 + * authors 2009-2023 * - Matt Young * - Mik Firestone * @@ -52,12 +52,12 @@ Salt::Salt(QString name) : Salt::Salt(NamedParameterBundle const & namedParameterBundle) : NamedEntity {namedParameterBundle}, - m_amount {namedParameterBundle(PropertyNames::Salt::amount ).toDouble()}, - m_add_to {static_cast(namedParameterBundle(PropertyNames::Salt::addTo).toInt())}, - m_type {static_cast(namedParameterBundle(PropertyNames::Salt::type).toInt())}, - m_amount_is_weight{namedParameterBundle(PropertyNames::Salt::amountIsWeight).toBool()}, - m_percent_acid {namedParameterBundle(PropertyNames::Salt::percentAcid ).toDouble()}, - m_is_acid {namedParameterBundle(PropertyNames::Salt::isAcid ).toBool()} { + m_amount {namedParameterBundle.val(PropertyNames::Salt::amount )}, + m_add_to {namedParameterBundle.val(PropertyNames::Salt::addTo )}, + m_type {namedParameterBundle.val(PropertyNames::Salt::type )}, + m_amount_is_weight{namedParameterBundle.val(PropertyNames::Salt::amountIsWeight)}, + m_percent_acid {namedParameterBundle.val(PropertyNames::Salt::percentAcid )}, + m_is_acid {namedParameterBundle.val(PropertyNames::Salt::isAcid )} { return; } @@ -72,6 +72,8 @@ Salt::Salt(Salt const & other) : return; } +Salt::~Salt() = default; + //================================"SET" METHODS================================= void Salt::setAmount(double var) { this->setAndNotify(PropertyNames::Salt::amount, this->m_amount, var); diff --git a/src/model/Salt.h b/src/model/Salt.h index 7d17b854a..25afbead0 100644 --- a/src/model/Salt.h +++ b/src/model/Salt.h @@ -1,6 +1,6 @@ /* * model/Salt.h is part of Brewtarget, and is Copyright the following - * authors 2009-2021 + * authors 2009-2023 * - Jeff Bailey * - Matt Young * - Mik Firestone @@ -79,13 +79,14 @@ class Salt : public NamedEntity { numTypes }; - Q_ENUMS(WhenToAdd Types) + Q_ENUM(WhenToAdd) + Q_ENUM(Types) Salt(QString name = ""); Salt(NamedParameterBundle const & namedParameterBundle); Salt(Salt const & other); - virtual ~Salt() = default; + virtual ~Salt(); // On a base or target profile, bicarbonate and alkalinity cannot both be used. I'm gonna have fun figuring that out //! \brief The amount of salt to be added (always a weight) diff --git a/src/model/Style.cpp b/src/model/Style.cpp index afde4f500..f4e0c99c9 100644 --- a/src/model/Style.cpp +++ b/src/model/Style.cpp @@ -56,7 +56,7 @@ Style::Style(QString t_name) : m_categoryNumber{""}, m_styleLetter {""}, m_styleGuide {""}, - m_type {Style::Lager}, + m_type {Style::Type::Lager}, m_ogMin {0.0}, m_ogMax {0.0}, m_fgMin {0.0}, @@ -76,6 +76,32 @@ Style::Style(QString t_name) : return; } +Style::Style(NamedParameterBundle const & namedParameterBundle) : + NamedEntity {namedParameterBundle}, + m_category {namedParameterBundle.val(PropertyNames::Style::category )}, + m_categoryNumber{namedParameterBundle.val(PropertyNames::Style::categoryNumber)}, + m_styleLetter {namedParameterBundle.val(PropertyNames::Style::styleLetter )}, + m_styleGuide {namedParameterBundle.val(PropertyNames::Style::styleGuide )}, + m_type {namedParameterBundle.val(PropertyNames::Style::type )}, + m_ogMin {namedParameterBundle.val(PropertyNames::Style::ogMin )}, + m_ogMax {namedParameterBundle.val(PropertyNames::Style::ogMax )}, + m_fgMin {namedParameterBundle.val(PropertyNames::Style::fgMin )}, + m_fgMax {namedParameterBundle.val(PropertyNames::Style::fgMax )}, + m_ibuMin {namedParameterBundle.val(PropertyNames::Style::ibuMin )}, + m_ibuMax {namedParameterBundle.val(PropertyNames::Style::ibuMax )}, + m_colorMin_srm {namedParameterBundle.val(PropertyNames::Style::colorMin_srm )}, + m_colorMax_srm {namedParameterBundle.val(PropertyNames::Style::colorMax_srm )}, + m_carbMin_vol {namedParameterBundle.val(PropertyNames::Style::carbMin_vol )}, + m_carbMax_vol {namedParameterBundle.val(PropertyNames::Style::carbMax_vol )}, + m_abvMin_pct {namedParameterBundle.val(PropertyNames::Style::abvMin_pct )}, + m_abvMax_pct {namedParameterBundle.val(PropertyNames::Style::abvMax_pct )}, + m_notes {namedParameterBundle.val(PropertyNames::Style::notes )}, + m_profile {namedParameterBundle.val(PropertyNames::Style::profile )}, + m_ingredients {namedParameterBundle.val(PropertyNames::Style::ingredients )}, + m_examples {namedParameterBundle.val(PropertyNames::Style::examples )} { + return; +} + Style::Style(Style const & other) : NamedEntity{other}, m_category {other.m_category }, @@ -102,32 +128,7 @@ Style::Style(Style const & other) : return; } -Style::Style(NamedParameterBundle const & namedParameterBundle) : - NamedEntity {namedParameterBundle}, - m_category {namedParameterBundle(PropertyNames::Style::category ).toString()}, - m_categoryNumber{namedParameterBundle(PropertyNames::Style::categoryNumber).toString()}, - m_styleLetter {namedParameterBundle(PropertyNames::Style::styleLetter ).toString()}, - m_styleGuide {namedParameterBundle(PropertyNames::Style::styleGuide ).toString()}, - m_type {static_cast(namedParameterBundle(PropertyNames::Style::type).toInt())}, - m_ogMin {namedParameterBundle(PropertyNames::Style::ogMin ).toDouble()}, - m_ogMax {namedParameterBundle(PropertyNames::Style::ogMax ).toDouble()}, - m_fgMin {namedParameterBundle(PropertyNames::Style::fgMin ).toDouble()}, - m_fgMax {namedParameterBundle(PropertyNames::Style::fgMax ).toDouble()}, - m_ibuMin {namedParameterBundle(PropertyNames::Style::ibuMin ).toDouble()}, - m_ibuMax {namedParameterBundle(PropertyNames::Style::ibuMax ).toDouble()}, - m_colorMin_srm {namedParameterBundle(PropertyNames::Style::colorMin_srm ).toDouble()}, - m_colorMax_srm {namedParameterBundle(PropertyNames::Style::colorMax_srm ).toDouble()}, - m_carbMin_vol {namedParameterBundle(PropertyNames::Style::carbMin_vol ).toDouble()}, - m_carbMax_vol {namedParameterBundle(PropertyNames::Style::carbMax_vol ).toDouble()}, - m_abvMin_pct {namedParameterBundle(PropertyNames::Style::abvMin_pct ).toDouble()}, - m_abvMax_pct {namedParameterBundle(PropertyNames::Style::abvMax_pct ).toDouble()}, - m_notes {namedParameterBundle(PropertyNames::Style::notes ).toString()}, - m_profile {namedParameterBundle(PropertyNames::Style::profile ).toString()}, - m_ingredients {namedParameterBundle(PropertyNames::Style::ingredients ).toString()}, - m_examples {namedParameterBundle(PropertyNames::Style::examples ).toString()} { - return; -} - +Style::~Style() = default; //==============================="SET" METHODS================================== void Style::setCategory(QString const & var) { @@ -224,7 +225,7 @@ QString Style::profile() const { return m_profile; } QString Style::ingredients() const { return m_ingredients; } QString Style::examples() const { return m_examples; } Style::Type Style::type() const { return m_type; } -const QString Style::typeString() const { return types.at(m_type); } +const QString Style::typeString() const { return types.at(static_cast(this->m_type)); } double Style::ogMin() const { return m_ogMin; } double Style::ogMax() const { return m_ogMax; } diff --git a/src/model/Style.h b/src/model/Style.h index cd1dabbbc..666b32073 100644 --- a/src/model/Style.h +++ b/src/model/Style.h @@ -1,6 +1,6 @@ /* * model/Style.h is part of Brewtarget, and is Copyright the following - * authors 2009-2021 + * authors 2009-2023 * - Jeff Bailey * - Matt Young * - Mik Firestone @@ -75,11 +75,11 @@ class Style : public NamedEntity { Style(NamedParameterBundle const & namedParameterBundle); Style(Style const & other); - virtual ~Style() = default; + virtual ~Style(); //! \brief The type of beverage. enum Type {Lager, Ale, Mead, Wheat, Mixed, Cider}; - Q_ENUMS( Type ) + Q_ENUM(Type) //! \brief The category. Q_PROPERTY( QString category READ category WRITE setCategory /*NOTIFY changed*/ /*changedCategory*/ ) diff --git a/src/model/Water.cpp b/src/model/Water.cpp index 98eacf642..1dda06d8d 100644 --- a/src/model/Water.cpp +++ b/src/model/Water.cpp @@ -44,20 +44,39 @@ ObjectStore & Water::getObjectStoreTypedInstance() const { Water::Water(QString name) : NamedEntity {name, true}, - m_amount {0.0}, - m_calcium_ppm {0.0}, - m_bicarbonate_ppm {0.0}, - m_sulfate_ppm {0.0}, - m_chloride_ppm {0.0}, - m_sodium_ppm {0.0}, - m_magnesium_ppm {0.0}, - m_ph {0.0}, - m_alkalinity {0.0}, - m_notes {""}, + m_amount {0.0 }, + m_calcium_ppm {0.0 }, + m_bicarbonate_ppm {0.0 }, + m_sulfate_ppm {0.0 }, + m_chloride_ppm {0.0 }, + m_sodium_ppm {0.0 }, + m_magnesium_ppm {0.0 }, + m_ph {0.0 }, + m_alkalinity {0.0 }, + m_notes {"" }, m_type {Water::Types::NONE}, - m_mash_ro {0.0}, - m_sparge_ro {0.0}, - m_alkalinity_as_hco3{true} { + m_mash_ro {0.0 }, + m_sparge_ro {0.0 }, + m_alkalinity_as_hco3{true } { + return; +} + +Water::Water(NamedParameterBundle const & namedParameterBundle) : + NamedEntity {namedParameterBundle}, + m_amount {namedParameterBundle.val(PropertyNames::Water::amount )}, + m_calcium_ppm {namedParameterBundle.val(PropertyNames::Water::calcium_ppm )}, + m_bicarbonate_ppm {namedParameterBundle.val(PropertyNames::Water::bicarbonate_ppm )}, + m_sulfate_ppm {namedParameterBundle.val(PropertyNames::Water::sulfate_ppm )}, + m_chloride_ppm {namedParameterBundle.val(PropertyNames::Water::chloride_ppm )}, + m_sodium_ppm {namedParameterBundle.val(PropertyNames::Water::sodium_ppm )}, + m_magnesium_ppm {namedParameterBundle.val(PropertyNames::Water::magnesium_ppm )}, + m_ph {namedParameterBundle.val(PropertyNames::Water::ph )}, + m_alkalinity {namedParameterBundle.val(PropertyNames::Water::alkalinity )}, + m_notes {namedParameterBundle.val(PropertyNames::Water::notes )}, + m_type {namedParameterBundle.val(PropertyNames::Water::type )}, + m_mash_ro {namedParameterBundle.val(PropertyNames::Water::mashRO )}, + m_sparge_ro {namedParameterBundle.val(PropertyNames::Water::spargeRO )}, + m_alkalinity_as_hco3{namedParameterBundle.val(PropertyNames::Water::alkalinityAsHCO3)} { return; } @@ -80,25 +99,6 @@ Water::Water(Water const& other) : return; } -Water::Water(NamedParameterBundle const & namedParameterBundle) : - NamedEntity {namedParameterBundle}, - m_amount {namedParameterBundle(PropertyNames::Water::amount).toDouble() }, - m_calcium_ppm {namedParameterBundle(PropertyNames::Water::calcium_ppm).toDouble() }, - m_bicarbonate_ppm {namedParameterBundle(PropertyNames::Water::bicarbonate_ppm).toDouble() }, - m_sulfate_ppm {namedParameterBundle(PropertyNames::Water::sulfate_ppm).toDouble() }, - m_chloride_ppm {namedParameterBundle(PropertyNames::Water::chloride_ppm).toDouble() }, - m_sodium_ppm {namedParameterBundle(PropertyNames::Water::sodium_ppm).toDouble() }, - m_magnesium_ppm {namedParameterBundle(PropertyNames::Water::magnesium_ppm).toDouble() }, - m_ph {namedParameterBundle(PropertyNames::Water::ph).toDouble() }, - m_alkalinity {namedParameterBundle(PropertyNames::Water::alkalinity).toDouble() }, - m_notes {namedParameterBundle(PropertyNames::Water::notes).toString() }, - m_type {static_cast(namedParameterBundle(PropertyNames::Water::type).toInt())}, - m_mash_ro {namedParameterBundle(PropertyNames::Water::mashRO).toDouble() }, - m_sparge_ro {namedParameterBundle(PropertyNames::Water::spargeRO).toDouble() }, - m_alkalinity_as_hco3{namedParameterBundle(PropertyNames::Water::alkalinityAsHCO3).toBool() } { - return; -} - void Water::swap(Water & other) noexcept { this->NamedEntity::swap(other); std::swap(this->m_amount , other.m_amount ); @@ -120,7 +120,8 @@ void Water::swap(Water & other) noexcept { Water::~Water() = default; /*Water::~Water() { - qDebug() << Q_FUNC_INFO << "Deleting Water #" << this->key() << ":" << this->name() << "@" << static_cast(this); + qDebug() << + Q_FUNC_INFO << "Deleting Water #" << this->key() << ":" << this->name() << "@" << static_cast(this); ObjectStoreWrapper::logDiagnostics(); return; }*/ @@ -163,7 +164,6 @@ Water & Water::operator=(Water other) { return *this; } - //================================"SET" METHODS================================= void Water::setAmount(double var) { this->setAndNotify(PropertyNames::Water::amount, this->m_amount, var); } void Water::setCalcium_ppm(double var) { this->setAndNotify(PropertyNames::Water::calcium_ppm, this->m_calcium_ppm, var); } diff --git a/src/model/Yeast.cpp b/src/model/Yeast.cpp index 20bb9f722..b8b5d0a88 100644 --- a/src/model/Yeast.cpp +++ b/src/model/Yeast.cpp @@ -55,9 +55,9 @@ ObjectStore & Yeast::getObjectStoreTypedInstance() const { Yeast::Yeast(QString name) : NamedEntityWithInventory{name, true}, - m_type {Yeast::Ale}, - m_form {Yeast::Liquid}, - m_flocculation {Yeast::Low}, + m_type {Yeast::Type::Ale}, + m_form {Yeast::Form::Liquid}, + m_flocculation {Yeast::Flocculation::Low}, m_amount {0.0}, m_amountIsWeight {false}, m_laboratory {""}, @@ -75,21 +75,21 @@ Yeast::Yeast(QString name) : Yeast::Yeast(NamedParameterBundle const & namedParameterBundle) : NamedEntityWithInventory{namedParameterBundle}, - m_type {static_cast(namedParameterBundle(PropertyNames::Yeast::type).toInt())}, - m_form {static_cast(namedParameterBundle(PropertyNames::Yeast::form).toInt())}, - m_flocculation {static_cast(namedParameterBundle(PropertyNames::Yeast::flocculation).toInt())}, - m_amount {namedParameterBundle(PropertyNames::Yeast::amount).toDouble()}, - m_amountIsWeight {namedParameterBundle(PropertyNames::Yeast::amountIsWeight).toBool()}, - m_laboratory {namedParameterBundle(PropertyNames::Yeast::laboratory).toString()}, - m_productID {namedParameterBundle(PropertyNames::Yeast::productID).toString()}, - m_minTemperature_c {namedParameterBundle(PropertyNames::Yeast::minTemperature_c).toDouble()}, - m_maxTemperature_c {namedParameterBundle(PropertyNames::Yeast::maxTemperature_c).toDouble()}, - m_attenuation_pct {namedParameterBundle(PropertyNames::Yeast::attenuation_pct).toDouble()}, - m_notes {namedParameterBundle(PropertyNames::Yeast::notes).toString()}, - m_bestFor {namedParameterBundle(PropertyNames::Yeast::bestFor).toString()}, - m_timesCultured {namedParameterBundle(PropertyNames::Yeast::timesCultured).toInt()}, - m_maxReuse {namedParameterBundle(PropertyNames::Yeast::maxReuse).toInt()}, - m_addToSecondary {namedParameterBundle(PropertyNames::Yeast::addToSecondary).toBool()} { + m_type {namedParameterBundle.val(PropertyNames::Yeast::type )}, + m_form {namedParameterBundle.val(PropertyNames::Yeast::form )}, + m_flocculation {namedParameterBundle.val(PropertyNames::Yeast::flocculation )}, + m_amount {namedParameterBundle.val(PropertyNames::Yeast::amount )}, + m_amountIsWeight {namedParameterBundle.val(PropertyNames::Yeast::amountIsWeight )}, + m_laboratory {namedParameterBundle.val(PropertyNames::Yeast::laboratory )}, + m_productID {namedParameterBundle.val(PropertyNames::Yeast::productID )}, + m_minTemperature_c {namedParameterBundle.val(PropertyNames::Yeast::minTemperature_c)}, + m_maxTemperature_c {namedParameterBundle.val(PropertyNames::Yeast::maxTemperature_c)}, + m_attenuation_pct {namedParameterBundle.val(PropertyNames::Yeast::attenuation_pct )}, + m_notes {namedParameterBundle.val(PropertyNames::Yeast::notes )}, + m_bestFor {namedParameterBundle.val(PropertyNames::Yeast::bestFor )}, + m_timesCultured {namedParameterBundle.val(PropertyNames::Yeast::timesCultured )}, + m_maxReuse {namedParameterBundle.val(PropertyNames::Yeast::maxReuse )}, + m_addToSecondary {namedParameterBundle.val(PropertyNames::Yeast::addToSecondary )} { return; } @@ -113,6 +113,8 @@ Yeast::Yeast(Yeast const & other) : return; } +Yeast::~Yeast() = default; + //============================="GET" METHODS==================================== QString Yeast::laboratory() const { return m_laboratory; } @@ -122,11 +124,11 @@ QString Yeast::notes() const { return m_notes; } QString Yeast::bestFor() const { return m_bestFor; } -const QString Yeast::typeString() const { return types.at(m_type); } +const QString Yeast::typeString() const { return types.at(static_cast(this->m_type)); } -const QString Yeast::formString() const { return forms.at(m_form); } +const QString Yeast::formString() const { return forms.at(static_cast(this->m_form)); } -const QString Yeast::flocculationString() const { return flocculations.at(m_flocculation); } +const QString Yeast::flocculationString() const { return flocculations.at(static_cast(this->m_flocculation)); } double Yeast::amount() const { return m_amount; } diff --git a/src/model/Yeast.h b/src/model/Yeast.h index d0429024a..93a627c4c 100644 --- a/src/model/Yeast.h +++ b/src/model/Yeast.h @@ -1,6 +1,6 @@ /* - * yeast.h is part of Brewtarget, and is Copyright the following - * authors 2009-2021 + * model/Yeast.h is part of Brewtarget, and is Copyright the following + * authors 2009-2023 * - Jeff Bailey * - Matt Young * - Mik Firestone @@ -74,13 +74,15 @@ class Yeast : public NamedEntityWithInventory { enum Form {Liquid, Dry, Slant, Culture}; //! \brief How flocculant the strain is. enum Flocculation {Low, Medium, High, Very_High}; // NOTE: BeerXML expects a space in "Very High", but not possible with enum. What to do? - Q_ENUMS( Type Form Flocculation ) + Q_ENUM(Type) + Q_ENUM(Form) + Q_ENUM(Flocculation) Yeast(QString name = ""); Yeast(NamedParameterBundle const & namedParameterBundle); Yeast(Yeast const & other); - virtual ~Yeast() = default; + virtual ~Yeast(); //! \brief The \c Type. Q_PROPERTY( Type type READ type WRITE setType /*NOTIFY changed*/ /*changedType*/ ) diff --git a/src/tableModels/BtTableModel.cpp b/src/tableModels/BtTableModel.cpp index 5d8039588..e1c117e13 100644 --- a/src/tableModels/BtTableModel.cpp +++ b/src/tableModels/BtTableModel.cpp @@ -27,7 +27,7 @@ #include "measurement/Measurement.h" #include "measurement/Unit.h" #include "measurement/UnitSystem.h" -#include "utils/OptionalToStream.h" +#include "utils/OptionalHelpers.h" #include "widgets/UnitAndScalePopUpMenu.h" BtTableModelRecipeObserver::BtTableModelRecipeObserver(QTableView * parent, diff --git a/src/tableModels/FermentableTableModel.cpp b/src/tableModels/FermentableTableModel.cpp index 0e3467483..9324fb065 100644 --- a/src/tableModels/FermentableTableModel.cpp +++ b/src/tableModels/FermentableTableModel.cpp @@ -54,13 +54,13 @@ FermentableTableModel::FermentableTableModel(QTableView* parent, bool editable) BtTableModelInventory{ parent, editable, - {{FERMNAMECOL, {tr("Name"), NonPhysicalQuantity::String, "" }}, - {FERMTYPECOL, {tr("Type"), NonPhysicalQuantity::String, "" }}, + {{FERMNAMECOL, {tr("Name"), NonPhysicalQuantity::String, "" }}, + {FERMTYPECOL, {tr("Type"), NonPhysicalQuantity::String, "" }}, {FERMAMOUNTCOL, {tr("Amount"), Measurement::PhysicalQuantity::Mass, "amount_kg" }}, {FERMINVENTORYCOL, {tr("Inventory"), Measurement::PhysicalQuantity::Mass, "inventory_kg"}}, - {FERMISMASHEDCOL, {tr("Method"), NonPhysicalQuantity::String, "" }}, - {FERMAFTERBOIL, {tr("Addition"), NonPhysicalQuantity::String, "" }}, - {FERMYIELDCOL, {tr("Yield %"), NonPhysicalQuantity::Percentage, "" }}, + {FERMISMASHEDCOL, {tr("Method"), NonPhysicalQuantity::String, "" }}, + {FERMAFTERBOIL, {tr("Addition"), NonPhysicalQuantity::String, "" }}, + {FERMYIELDCOL, {tr("Yield %"), NonPhysicalQuantity::Percentage, "" }}, {FERMCOLORCOL, {tr("Color"), Measurement::PhysicalQuantity::Color, "color_srm" }}} }, BtTableModelData{}, @@ -443,8 +443,8 @@ bool FermentableTableModel::setData(QModelIndex const & index, Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Mass, this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity, - tr("Change Inventory Amount") + this->getForcedRelativeScaleForColumn(column)).quantity(), + tr("Change Fermentable Inventory Amount") ); } break; @@ -459,7 +459,7 @@ bool FermentableTableModel::setData(QModelIndex const & index, Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Mass, this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity, + this->getForcedRelativeScaleForColumn(column)).quantity(), tr("Change Fermentable Amount") ); if (rowCount() > 0) { @@ -507,7 +507,7 @@ bool FermentableTableModel::setData(QModelIndex const & index, Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Color, this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity, + this->getForcedRelativeScaleForColumn(column)).quantity(), tr("Change Color") ); } @@ -620,28 +620,28 @@ void FermentableItemDelegate::setModelData(QWidget *editor, QAbstractItemModel * int value = box->currentIndex(); int ndx = model->data(index, Qt::UserRole).toInt(); - // Only do something when something needs to be done + // Only do something when something needs to be done if ( value != ndx ) model->setData(index, value, Qt::EditRole); - } + } else if (col == FERMISMASHEDCOL || col == FERMAFTERBOIL ) { QComboBox* box = qobject_cast(editor); int value = box->currentIndex(); int ndx = model->data(index, Qt::UserRole).toInt(); - // Only do something when something needs to be done + // Only do something when something needs to be done if ( value != ndx ) model->setData(index, value, Qt::EditRole); - } + } else { QLineEdit* line = qobject_cast(editor); if ( line->isModified() ) model->setData(index, line->text(), Qt::EditRole); + } } -} void FermentableItemDelegate::updateEditorGeometry(QWidget * editor, QStyleOptionViewItem const & option, diff --git a/src/tableModels/HopTableModel.cpp b/src/tableModels/HopTableModel.cpp index 1ee67cd6b..eafc4ee70 100644 --- a/src/tableModels/HopTableModel.cpp +++ b/src/tableModels/HopTableModel.cpp @@ -395,7 +395,7 @@ bool HopTableModel::setData(const QModelIndex & index, const QVariant & value, i Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Mass, this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity, + this->getForcedRelativeScaleForColumn(column)).quantity(), tr("Change Hop Inventory Amount") ); } @@ -409,7 +409,7 @@ bool HopTableModel::setData(const QModelIndex & index, const QVariant & value, i Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Mass, this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity, + this->getForcedRelativeScaleForColumn(column)).quantity(), tr("Change Hop Amount") ); } @@ -441,7 +441,7 @@ bool HopTableModel::setData(const QModelIndex & index, const QVariant & value, i Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Time, std::nullopt, - this->getForcedRelativeScaleForColumn(column)).quantity, + this->getForcedRelativeScaleForColumn(column)).quantity(), tr("Change Hop Time") ); } diff --git a/src/tableModels/MashStepTableModel.cpp b/src/tableModels/MashStepTableModel.cpp index 58c4a9f99..2afb9ca0e 100644 --- a/src/tableModels/MashStepTableModel.cpp +++ b/src/tableModels/MashStepTableModel.cpp @@ -368,7 +368,7 @@ bool MashStepTableModel::setData(QModelIndex const & index, QVariant const & val Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Volume, this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity, + this->getForcedRelativeScaleForColumn(column)).quantity(), tr("Change Mash Step Decoction Amount") ); } else { @@ -378,7 +378,7 @@ bool MashStepTableModel::setData(QModelIndex const & index, QVariant const & val Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Volume, this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity, + this->getForcedRelativeScaleForColumn(column)).quantity(), tr("Change Mash Step Infuse Amount") ); } @@ -394,7 +394,7 @@ bool MashStepTableModel::setData(QModelIndex const & index, QVariant const & val Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Temperature, this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity, + this->getForcedRelativeScaleForColumn(column)).quantity(), tr("Change Mash Step Infuse Temp") ); return true; @@ -413,7 +413,7 @@ bool MashStepTableModel::setData(QModelIndex const & index, QVariant const & val Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Temperature, this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity, + this->getForcedRelativeScaleForColumn(column)).quantity(), tr("Change Mash Step Temp") ); new SimpleUndoableUpdate(*row, @@ -421,7 +421,7 @@ bool MashStepTableModel::setData(QModelIndex const & index, QVariant const & val Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Temperature, this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity, + this->getForcedRelativeScaleForColumn(column)).quantity(), tr("Change Mash Step End Temp"), targetTempUpdate); MainWindow::instance().doOrRedoUpdate(targetTempUpdate); @@ -437,7 +437,7 @@ bool MashStepTableModel::setData(QModelIndex const & index, QVariant const & val Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Time, this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity, + this->getForcedRelativeScaleForColumn(column)).quantity(), tr("Change Mash Step Time") ); return true; diff --git a/src/tableModels/MiscTableModel.cpp b/src/tableModels/MiscTableModel.cpp index c2aacbc72..412cb625d 100644 --- a/src/tableModels/MiscTableModel.cpp +++ b/src/tableModels/MiscTableModel.cpp @@ -343,7 +343,7 @@ bool MiscTableModel::setData(QModelIndex const & index, Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Time, this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity, + this->getForcedRelativeScaleForColumn(column)).quantity(), tr("Change Misc Time") ); break; @@ -357,7 +357,7 @@ bool MiscTableModel::setData(QModelIndex const & index, Measurement::qStringToSI(value.toString(), physicalQuantity, this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity, + this->getForcedRelativeScaleForColumn(column)).quantity(), tr("Change Misc Inventory Amount") ); break; @@ -371,7 +371,7 @@ bool MiscTableModel::setData(QModelIndex const & index, Measurement::qStringToSI(value.toString(), physicalQuantity, this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity, + this->getForcedRelativeScaleForColumn(column)).quantity(), tr("Change Misc Amount") ); break; diff --git a/src/tableModels/SaltTableModel.cpp b/src/tableModels/SaltTableModel.cpp index 104f66b64..5c0c11fb3 100644 --- a/src/tableModels/SaltTableModel.cpp +++ b/src/tableModels/SaltTableModel.cpp @@ -488,7 +488,7 @@ bool SaltTableModel::setData(QModelIndex const & index, QVariant const & value, row->amountIsWeight() ? Measurement::PhysicalQuantity::Mass : Measurement::PhysicalQuantity::Volume, this->getForcedSystemOfMeasurementForColumn(column), this->getForcedRelativeScaleForColumn(column) - ).quantity + ).quantity() ); } break; diff --git a/src/tableModels/WaterTableModel.cpp b/src/tableModels/WaterTableModel.cpp index 1dde0178c..51c97a576 100644 --- a/src/tableModels/WaterTableModel.cpp +++ b/src/tableModels/WaterTableModel.cpp @@ -268,7 +268,7 @@ bool WaterTableModel::setData(QModelIndex const & index, QVariant const & value, row->setAmount(Measurement::qStringToSI(value.toString(), Measurement::PhysicalQuantity::Volume, this->getForcedSystemOfMeasurementForColumn(column), - this->getForcedRelativeScaleForColumn(column)).quantity); + this->getForcedRelativeScaleForColumn(column)).quantity()); break; case WATERCALCIUMCOL: row->setCalcium_ppm(Localization::toDouble(value.toString(), Q_FUNC_INFO)); diff --git a/src/tableModels/YeastTableModel.cpp b/src/tableModels/YeastTableModel.cpp index cd9f4e3f6..dde0d230f 100644 --- a/src/tableModels/YeastTableModel.cpp +++ b/src/tableModels/YeastTableModel.cpp @@ -395,7 +395,7 @@ bool YeastTableModel::setData(QModelIndex const & index, QVariant const & value, row->amountIsWeight() ? Measurement::PhysicalQuantity::Mass : Measurement::PhysicalQuantity::Volume, this->getForcedSystemOfMeasurementForColumn(column), this->getForcedRelativeScaleForColumn(column) - ).quantity, + ).quantity(), tr("Change Yeast Amount") ); break; diff --git a/src/unitTests/Testing.cpp b/src/unitTests/Testing.cpp index 24c636541..fb43920a5 100644 --- a/src/unitTests/Testing.cpp +++ b/src/unitTests/Testing.cpp @@ -1,6 +1,6 @@ /* * Testing.cpp is part of Brewtarget, and is Copyright the following - * authors 2009-2022 + * authors 2009-2023 * - Mattias Måhl * - Matt Young * - Philip Lee @@ -41,6 +41,7 @@ #include "Algorithms.h" #include "config.h" #include "database/ObjectStoreWrapper.h" +#include "Localization.h" #include "Logging.h" #include "measurement/Measurement.h" #include "measurement/Unit.h" @@ -50,6 +51,7 @@ #include "model/Hop.h" #include "model/Mash.h" #include "model/MashStep.h" +#include "model/NamedParameterBundle.h" #include "model/Recipe.h" #include "PersistentSettings.h" @@ -311,7 +313,7 @@ Testing::~Testing() { } } return; -}; +} // // If you're building with CMake: @@ -421,8 +423,8 @@ void Testing::initTestCase() { return; } -void Testing::recipeCalcTest_allGrain() -{ +void Testing::recipeCalcTest_allGrain() { + // .:TODO:. Would be good to fix and reinstate this test... return; double const grain_kg = 5.0; double const conversion_l = grain_kg * 2.8; // 2.8 L/kg mash thickness @@ -506,8 +508,8 @@ void Testing::recipeCalcTest_allGrain() QVERIFY2( fuzzyComp(rec->color_srm(), srm, srm*0.1), "Wrong color calculation" ); } -void Testing::postBoilLossOgTest() -{ +void Testing::postBoilLossOgTest() { + // .:TODO:. Would be good to fix and reinstate this test... return; double const grain_kg = 5.0; Recipe* recNoLoss = new Recipe(QString("TestRecipe_noLoss")); @@ -584,8 +586,8 @@ void Testing::testUnitConversions() { // However, this didn't seem to have any effect on a French locale Windows. // So Plan B is to construct test data based on the current locale settings. // - QString const decimalSeparator = QLocale::system().decimalPoint(); - QString const thousandsSeparator = QLocale::system().groupSeparator(); + QString const decimalSeparator = Localization::getLocale().decimalPoint(); + QString const thousandsSeparator = Localization::getLocale().groupSeparator(); qDebug() << Q_FUNC_INFO << "Decimal separator is " << decimalSeparator << " | Thousands separator is " << thousandsSeparator; QString testInput{}; @@ -594,7 +596,7 @@ void Testing::testUnitConversions() { testInput.clear(); testInputAsStream << "5" << decimalSeparator << "500 gal"; QVERIFY2(fuzzyComp(Measurement::UnitSystems::volume_UsCustomary.qstringToSI(testInput, // "5.500 gal" - Measurement::Units::liters).quantity, + Measurement::Units::liters).quantity(), 20.820, 0.001), "Unit conversion error (US gallons to Litres v1)"); @@ -602,7 +604,7 @@ void Testing::testUnitConversions() { testInput.clear(); testInputAsStream << "5" << decimalSeparator << "500"; QVERIFY2(fuzzyComp(Measurement::UnitSystems::volume_UsCustomary.qstringToSI(testInput, // "5.500" - Measurement::Units::us_gallons).quantity, + Measurement::Units::us_gallons).quantity(), 20.820, 0.001), "Unit conversion error (US gallons to Litres v2)"); @@ -610,7 +612,7 @@ void Testing::testUnitConversions() { testInput.clear(); testInputAsStream << "5" << decimalSeparator << "500 gal"; QVERIFY2(fuzzyComp(Measurement::qStringToSI(testInput, // "5.500 gal" - Measurement::PhysicalQuantity::Volume).quantity, + Measurement::PhysicalQuantity::Volume).quantity(), 20.820, 0.001), "Unit conversion error (US gallons to Litres v3)"); @@ -618,7 +620,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::sp_grav).quantity(), 1.040, 0.001), "Unit conversion error (Plato to SG)"); @@ -627,7 +629,7 @@ void Testing::testUnitConversions() { testInputAsStream << "1" << thousandsSeparator << "083 ebc"; QVERIFY2( fuzzyComp(Measurement::UnitSystems::color_StandardReferenceMethod.qstringToSI(testInput, // "1,083 ebc" - Measurement::Units::srm).quantity, + Measurement::Units::srm).quantity(), 550, 1), "Unit conversion error (EBC to SRM)" @@ -636,6 +638,73 @@ void Testing::testUnitConversions() { return; } +void Testing::testNamedParameterBundle() { + + // + // First some basic tests + // + NamedParameterBundle npb; + + BtStringConst const myInt{"myInt"}; + npb.insert(myInt, 42); + QVERIFY2(npb.get(myInt).toInt() == 42, "Error retrieving int"); + + BtStringConst const myFalseBool{"myFalseBool"}; + npb.insert(myFalseBool, false); + QVERIFY2(!npb.get(myFalseBool).toBool(), "Error retrieving false bool"); + + BtStringConst const myTrueBool{"myTrueBool"}; + npb.insert(myTrueBool, true); + QVERIFY2(npb.get(myTrueBool).toBool(), "Error retrieving true bool"); + + BtStringConst const myString{"myString"}; + npb.insert(myString, "Sing a string of sixpence"); + QVERIFY2(npb.get(myString).toString() == "Sing a string of sixpence", "Error retrieving string"); + + BtStringConst const myDouble{"myDouble"}; + npb.insert(myDouble, 3.1415926535897932384626433); + QVERIFY2(fuzzyComp(npb.get(myDouble).toDouble(), + 3.1415926535897932384626433, + 0.0000000001), + "Error retrieving double"); +/* + // + // Test that we can store an int and get it back as a strongly-typed enum + // + npb.insert(PropertyNames::Hop::type, static_cast(Hop::Type::AromaAndFlavor)); + Hop::Type retrievedHopType = npb.val(PropertyNames::Hop::type); + QVERIFY2(retrievedHopType == Hop::Type::AromaAndFlavor, "Int -> Strongly-typed enum failed"); + + // + // Now test explicitly nullable fields -- UNCOMMENT THIS ONCE OPTIONAL FIELDS IMPLEMENTED IN BREWTARGET + // + QVERIFY2( + npb.optEnumVal(PropertyNames::Fermentable::grainGroup) == std::nullopt, + "Error getting default value for optional enum" + ); + std::optional myOptional{static_cast(Fermentable::GrainGroup::Smoked)}; + QVariant myVariant = QVariant::fromValue(myOptional); + npb.insert(PropertyNames::Fermentable::grainGroup, myVariant); + + QVariant retrievedValueA = npb.get(PropertyNames::Fermentable::grainGroup); + std::optional castValueA = retrievedValueA.value< std::optional >(); + QVERIFY2(castValueA.has_value(), "Error retrieving optional enum"); + QVERIFY2(castValueA.value() == static_cast(Fermentable::GrainGroup::Smoked), + "Error retrieving optional enum as int"); + + auto retrievedValueB = npb.optEnumVal(PropertyNames::Fermentable::grainGroup); + qDebug() << + Q_FUNC_INFO << "retrievedValueB=" << (retrievedValueB.has_value() ? static_cast(*retrievedValueB) : -999) << + "; Fermentable::GrainGroup::Smoked = " << static_cast(Fermentable::GrainGroup::Smoked); + QVERIFY2(retrievedValueB.has_value(), "Expected value, got none"); + QVERIFY2( + retrievedValueB.value() == Fermentable::GrainGroup::Smoked, + "Error retrieving optional enum" + ); +*/ + return; +} + void Testing::testAlgorithms() { for (auto const & ii : sgBrixEquivalances) { qDebug() << @@ -712,6 +781,10 @@ void Testing::cleanupTestCase() void Testing::pstdintTest() { + // + // .:TBD:. I'm not sure this is the most useful of unit tests. We're effectively checking that one tiny part of the + // C++11 standard is correctly implemented by the compiler. + // QVERIFY( sizeof(int8_t) == 1 ); QVERIFY( sizeof(int16_t) == 2 ); QVERIFY( sizeof(int32_t) == 4 ); @@ -729,11 +802,11 @@ void Testing::pstdintTest() { } -void Testing::runTest() -{ +void Testing::runTest() { + // .:TBD:. We should probably retire this function... :o) QVERIFY( 1==1 ); /* - MainWindow* mw = Application::mainWindow(); + MainWindow& mw = Application::mainWindow(); QVERIFY( mw ); */ } diff --git a/src/unitTests/Testing.h b/src/unitTests/Testing.h index 176aa23bb..31fc7412d 100644 --- a/src/unitTests/Testing.h +++ b/src/unitTests/Testing.h @@ -76,6 +76,9 @@ private slots: //! \brief Verify conversion between US Customary & Metric units etc void testUnitConversions(); + //! \brief Test that NamedParameterBundle is behaving as we expect + void testNamedParameterBundle(); + /** * \brief Verify other conversions that warrant their own algorithms. * diff --git a/src/utils/OptionalHelpers.h b/src/utils/OptionalHelpers.h new file mode 100644 index 000000000..509c289db --- /dev/null +++ b/src/utils/OptionalHelpers.h @@ -0,0 +1,117 @@ +/* + * utils/OptionalHelpers.h is part of Brewtarget, and is copyright the following + * authors 2022: + * - Matt Young + * + * Brewtarget is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Brewtarget is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef UTILS_OPTIONALTOSTREAM_H +#define UTILS_OPTIONALTOSTREAM_H +#pragma once + +#include + +#include + +/** + * \brief A set of utilities that help us deal with std::optional values + */ +namespace Optional { + /** + * \brief Helper function called from \c ObjectStore::impl::unwrapAndMapAsNeeded and other places to convert a + * \c QVariant containing \c std::optional to either a \c QVariant containing \c T or a null \c QVariant + */ + template + void removeOptionalWrapper(QVariant & propertyValue) { + auto val = propertyValue.value >(); + if (val.has_value()) { + propertyValue = QVariant::fromValue(val.value()); + } else { + propertyValue = QVariant(); + } + return; + } + + /** + * \brief Helper function called from \c ObjectStore::impl::wrapAndUnmapAsNeeded and other places to convert a + * \c QVariant that is either null or contains \c T to a \c QVariant containing \c std::optional + */ + template + void insertOptionalWrapper(QVariant & propertyValue) { + if (propertyValue.isNull()) { + propertyValue = QVariant::fromValue(std::optional()); + } else { + propertyValue = QVariant::fromValue(std::optional(propertyValue.value())); + } + return; + } + + /** + * \brief Create a \c QVariant from a raw value, including the \c std::optional wrapper if needed + */ + template + QVariant variantFromRaw(T const & rawValue, bool const propertyIsOptional) { + if (propertyIsOptional) { + return QVariant::fromValue< std::optional >(rawValue); + } + return QVariant::fromValue(rawValue); + } + + /** + * \brief Convenience wrapper function that calls \c QVariant::canConvert() for either \c T or \c std::optional + * as appropriate. + */ + template + bool canConvert(QVariant const & propertyValue, bool const propertyIsOptional) { + if (propertyIsOptional) { + return propertyValue.canConvert< std::optional >(); + } + return propertyValue.canConvert(); + } + + /** + * \brief Remove the \c std::optional wrapper, if it is present, from inside a \c QVariant + * + * \return \c false if the contained value is optional and not present, \c true otherwise + */ + template + bool removeOptionalWrapperIfPresent(QVariant & propertyValue, bool const propertyIsOptional) { + // It is a coding error to pass a QVariant that can't be converted to (optional) T + Q_ASSERT(canConvert(propertyValue, propertyIsOptional)); + if (propertyIsOptional) { + removeOptionalWrapper(propertyValue); + return !propertyValue.isNull(); + } + return true; + } + +} + +/** + * \brief Convenience function to allow output of \c std::optional to \c QDebug or \c QTextStream stream + * + * (For some reason, \c QDebug does not inherit from \c QTextStream so we template the stream class as well as + * what we're outputting.) + */ +template +S & operator<<(S & stream, std::optional optionalItem) { + if (optionalItem) { + stream << *optionalItem; + } else { + stream << "NULL"; + } + return stream; +} + +#endif diff --git a/src/utils/OptionalToStream.h b/src/utils/OptionalToStream.h deleted file mode 100644 index b5dc3897d..000000000 --- a/src/utils/OptionalToStream.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * utils/OptionalToStream.h is part of Brewtarget, and is copyright the following - * authors 2022: - * - Matt Young - * - * Brewtarget is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Brewtarget is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -#ifndef UTILS_OPTIONALTOSTREAM_H -#define UTILS_OPTIONALTOSTREAM_H -#pragma once - -#include - -/** - * \brief Convenience function to allow output of \c std::optional to \c QDebug or \c QTextStream stream - * - * (For some reason, \c QDebug does not inherit from \c QTextStream so we template the stream class as well as - * what we're outputting.) - */ -template -S & operator<<(S & stream, std::optional optionalItem) { - if (optionalItem) { - stream << *optionalItem; - } else { - stream << "NULL"; - } - return stream; -} - -#endif diff --git a/src/xml/XmlRecipeRecord.cpp b/src/xml/XmlRecipeRecord.cpp index c5bdca644..529f3dc1d 100644 --- a/src/xml/XmlRecipeRecord.cpp +++ b/src/xml/XmlRecipeRecord.cpp @@ -1,6 +1,6 @@ /* * xml/XmlRecipeRecord.cpp is part of Brewtarget, and is Copyright the following - * authors 2020-2022 + * authors 2020-2023 * - Matt Young * * Brewtarget is free software: you can redistribute it and/or modify @@ -40,29 +40,29 @@ namespace { return; } template<> void setAmountsEtc(Hop & hop, NamedParameterBundle const & npb) { - hop.setAmount_kg(npb(PropertyNames::Hop::amount_kg).toDouble()); - hop.setTime_min( npb(PropertyNames::Hop::time_min).toDouble()); + hop.setAmount_kg(npb.val(PropertyNames::Hop::amount_kg)); + hop.setTime_min (npb.val(PropertyNames::Hop::time_min )); return; } template<> void setAmountsEtc(Fermentable & fermentable, NamedParameterBundle const & npb) { - fermentable.setAmount_kg( npb(PropertyNames::Fermentable::amount_kg).toDouble()); - fermentable.setAddAfterBoil(npb(PropertyNames::Fermentable::addAfterBoil).toBool()); - fermentable.setIsMashed( npb(PropertyNames::Fermentable::isMashed).toBool()); + fermentable.setAmount_kg (npb.val(PropertyNames::Fermentable::amount_kg )); + fermentable.setAddAfterBoil(npb.val(PropertyNames::Fermentable::addAfterBoil)); + fermentable.setIsMashed (npb.val(PropertyNames::Fermentable::isMashed )); return; } template<> void setAmountsEtc(Misc & misc, NamedParameterBundle const & npb) { - misc.setAmount( npb(PropertyNames::Misc::amount).toDouble()); - misc.setAmountIsWeight(npb(PropertyNames::Misc::amountIsWeight).toBool()); - misc.setTime( npb(PropertyNames::Misc::time).toDouble()); + misc.setAmount (npb.val(PropertyNames::Misc::amount )); + misc.setAmountIsWeight(npb.val(PropertyNames::Misc::amountIsWeight)); + misc.setTime (npb.val(PropertyNames::Misc::time )); return; } template<> void setAmountsEtc(Yeast & yeast, NamedParameterBundle const & npb) { - yeast.setAmount( npb(PropertyNames::Yeast::amount).toDouble()); - yeast.setAmountIsWeight(npb(PropertyNames::Yeast::amountIsWeight).toBool()); + yeast.setAmount (npb.val(PropertyNames::Yeast::amount )); + yeast.setAmountIsWeight(npb.val(PropertyNames::Yeast::amountIsWeight)); return; } template<> void setAmountsEtc(Water & water, NamedParameterBundle const & npb) { - water.setAmount( npb(PropertyNames::Water::amount).toDouble()); + water.setAmount(npb.val(PropertyNames::Water::amount)); return; } diff --git a/translations/bt_ca.ts b/translations/bt_ca.ts index a2aa33eca..28527f45c 100644 --- a/translations/bt_ca.ts +++ b/translations/bt_ca.ts @@ -1334,10 +1334,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1358,6 +1354,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -3120,10 +3120,6 @@ Log file may contain more details. Mixed Mixte - - DiastaticPower - - Unknown Desconegut @@ -3302,6 +3298,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_cs.ts b/translations/bt_cs.ts index 50be024e4..2731cc000 100644 --- a/translations/bt_cs.ts +++ b/translations/bt_cs.ts @@ -1294,10 +1294,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1318,6 +1314,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -3080,10 +3080,6 @@ Log file may contain more details. Mixed Mix - - DiastaticPower - - Unknown Neznámý @@ -3262,6 +3258,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_de.ts b/translations/bt_de.ts index 515806127..fcb2f89ec 100644 --- a/translations/bt_de.ts +++ b/translations/bt_de.ts @@ -1318,10 +1318,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1342,6 +1338,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -3104,10 +3104,6 @@ Log file may contain more details. Mixed Gemischt - - DiastaticPower - - Unknown unbekannt @@ -3286,6 +3282,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_el.ts b/translations/bt_el.ts index 3927206b9..46c203bca 100644 --- a/translations/bt_el.ts +++ b/translations/bt_el.ts @@ -1294,10 +1294,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1318,6 +1314,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -3080,10 +3080,6 @@ Log file may contain more details. Mixed Ανάμεικτη - - DiastaticPower - - Unknown Άγνωστο @@ -3262,6 +3258,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_en.ts b/translations/bt_en.ts index 7db68b9e6..ff6f88d94 100644 --- a/translations/bt_en.ts +++ b/translations/bt_en.ts @@ -940,10 +940,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -964,6 +960,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -2626,10 +2626,6 @@ Log file may contain more details. Mixed - - DiastaticPower - - Unknown @@ -2808,6 +2804,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_es.ts b/translations/bt_es.ts index fcb961656..204c23a82 100644 --- a/translations/bt_es.ts +++ b/translations/bt_es.ts @@ -1334,10 +1334,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1358,6 +1354,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -3120,10 +3120,6 @@ Log file may contain more details. Mixed Mezclado - - DiastaticPower - - Unknown Desconocido @@ -3302,6 +3298,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_et.ts b/translations/bt_et.ts index d46122bf4..e57e8f7d2 100644 --- a/translations/bt_et.ts +++ b/translations/bt_et.ts @@ -991,10 +991,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1015,6 +1011,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -2677,10 +2677,6 @@ Log file may contain more details. Mixed - - DiastaticPower - - Unknown @@ -2859,6 +2855,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_eu.ts b/translations/bt_eu.ts index 15f7d3a44..f34eb08c1 100644 --- a/translations/bt_eu.ts +++ b/translations/bt_eu.ts @@ -999,10 +999,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1023,6 +1019,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -2685,10 +2685,6 @@ Log file may contain more details. Mixed - - DiastaticPower - - Unknown @@ -2867,6 +2863,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_fr.ts b/translations/bt_fr.ts index 4f6587067..02d047003 100644 --- a/translations/bt_fr.ts +++ b/translations/bt_fr.ts @@ -1334,10 +1334,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1358,6 +1354,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -3120,10 +3120,6 @@ Log file may contain more details. Mixed Mixte - - DiastaticPower - - Unknown Inconnu @@ -3302,6 +3298,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_gl.ts b/translations/bt_gl.ts index 1ee60293e..0cf51c33a 100644 --- a/translations/bt_gl.ts +++ b/translations/bt_gl.ts @@ -1114,10 +1114,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1138,6 +1134,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -2800,10 +2800,6 @@ Log file may contain more details. Mixed - - DiastaticPower - - Unknown Descoñecido @@ -2982,6 +2978,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_hu.ts b/translations/bt_hu.ts index d7e4dd789..06167f576 100644 --- a/translations/bt_hu.ts +++ b/translations/bt_hu.ts @@ -1322,10 +1322,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1346,6 +1342,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -3108,10 +3108,6 @@ Log file may contain more details. Mixed - - DiastaticPower - - Unknown ismeretlen @@ -3290,6 +3286,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_it.ts b/translations/bt_it.ts index b1036d1d2..cc8ac6997 100644 --- a/translations/bt_it.ts +++ b/translations/bt_it.ts @@ -1346,10 +1346,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1370,6 +1366,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -3124,10 +3124,6 @@ Log file may contain more details. Mixed Mix - - DiastaticPower - - Unknown Sconosciuto @@ -3306,6 +3302,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_lv.ts b/translations/bt_lv.ts index dc0508a62..6ce324b50 100644 --- a/translations/bt_lv.ts +++ b/translations/bt_lv.ts @@ -1054,10 +1054,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1078,6 +1074,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -2740,10 +2740,6 @@ Log file may contain more details. Mixed - - DiastaticPower - - Unknown Nezināms @@ -2922,6 +2918,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_nb.ts b/translations/bt_nb.ts index 60f79c922..fc4280b1e 100644 --- a/translations/bt_nb.ts +++ b/translations/bt_nb.ts @@ -1294,10 +1294,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1318,6 +1314,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -3080,10 +3080,6 @@ Log file may contain more details. Mixed Blandet - - DiastaticPower - - Unknown Ukjent @@ -3262,6 +3258,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_nl.ts b/translations/bt_nl.ts index da366e337..680028ed1 100644 --- a/translations/bt_nl.ts +++ b/translations/bt_nl.ts @@ -1318,10 +1318,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1342,6 +1338,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -3131,10 +3131,6 @@ Foutmelding: Mixed Gemengd - - DiastaticPower - - Unknown Onbekend @@ -3297,6 +3293,30 @@ Foutmelding: Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_pl.ts b/translations/bt_pl.ts index 4d17eec3f..0af6d8d38 100644 --- a/translations/bt_pl.ts +++ b/translations/bt_pl.ts @@ -1294,10 +1294,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1318,6 +1314,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -3080,10 +3080,6 @@ Log file may contain more details. Mixed Mieszane - - DiastaticPower - - Unknown Nieznane @@ -3262,6 +3258,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_pt.ts b/translations/bt_pt.ts index a7c19ba7a..a40a59c95 100644 --- a/translations/bt_pt.ts +++ b/translations/bt_pt.ts @@ -1326,10 +1326,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1350,6 +1346,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -3096,10 +3096,6 @@ Log file may contain more details. Mixed Mista - - DiastaticPower - - Unknown Desconhecido @@ -3278,6 +3274,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_ru.ts b/translations/bt_ru.ts index 392a8cc31..0c1abb120 100644 --- a/translations/bt_ru.ts +++ b/translations/bt_ru.ts @@ -1334,10 +1334,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1358,6 +1354,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -3124,10 +3124,6 @@ Log file may contain more details. Mixed Смешанный - - DiastaticPower - - Unknown @@ -3306,6 +3302,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_sr.ts b/translations/bt_sr.ts index b275a7253..49b4b6d7f 100644 --- a/translations/bt_sr.ts +++ b/translations/bt_sr.ts @@ -1178,10 +1178,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1202,6 +1198,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -2948,10 +2948,6 @@ Log file may contain more details. Mixed - - DiastaticPower - - Unknown Непознато @@ -3130,6 +3126,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_sv.ts b/translations/bt_sv.ts index bf13928fb..c52a34357 100644 --- a/translations/bt_sv.ts +++ b/translations/bt_sv.ts @@ -1318,10 +1318,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1342,6 +1338,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -3088,10 +3088,6 @@ Log file may contain more details. Mixed Blandad - - DiastaticPower - - Unknown Okänd @@ -3270,6 +3266,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_tr.ts b/translations/bt_tr.ts index a35177316..f823982cc 100644 --- a/translations/bt_tr.ts +++ b/translations/bt_tr.ts @@ -997,7 +997,7 @@ Log file may contain more details. Change Inventory Amount - Envanter Miktarını Değiştir + Envanter Miktarını Değiştir Change Fermentable Amount @@ -1019,6 +1019,10 @@ Log file may contain more details. Change Color Rengi Değiştir + + Change Fermentable Inventory Amount + + Hop @@ -2720,7 +2724,7 @@ Günlük dosyası daha fazla detay içerebilir. DiastaticPower - Diastatik Güç + Diastatik Güç Unknown @@ -2902,6 +2906,30 @@ Günlük dosyası daha fazla detay içerebilir. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/translations/bt_zh.ts b/translations/bt_zh.ts index c4c6078d3..7f095dfc5 100644 --- a/translations/bt_zh.ts +++ b/translations/bt_zh.ts @@ -1290,10 +1290,6 @@ Log file may contain more details. Change Fermentable Type - - Change Inventory Amount - - Change Fermentable Amount @@ -1314,6 +1310,10 @@ Log file may contain more details. Change Color + + Change Fermentable Inventory Amount + + Hop @@ -3072,10 +3072,6 @@ Log file may contain more details. Mixed 混合Mixed - - DiastaticPower - - Unknown @@ -3254,6 +3250,30 @@ Log file may contain more details. Concentration Mass Per Volume + + Diastatic Power + + + + Specific Heat Capacity + + + + Specific Heat Capacity Calories per + + + + Specific Heat Capacity Joules per + + + + c/g·C + + + + J/kg·K + + Recipe diff --git a/ui/equipmentEditor.ui b/ui/equipmentEditor.ui index f550418f1..b770f102d 100644 --- a/ui/equipmentEditor.ui +++ b/ui/equipmentEditor.ui @@ -464,7 +464,7 @@ - + tunSpecificHeat_calGC @@ -642,6 +642,14 @@ lineChanged(PreviousScaleInfo) + + BtDimensionlessEdit + QLineEdit +
    BtLineEdit.h
    + + lineChanged(PreviousScaleInfo) + +
    tabWidget_editor