diff --git a/.github/workflows/linux-ubuntu.yml b/.github/workflows/linux-ubuntu.yml index 8ee7c9ba..553474f6 100644 --- a/.github/workflows/linux-ubuntu.yml +++ b/.github/workflows/linux-ubuntu.yml @@ -91,8 +91,6 @@ jobs: working-directory: ${{github.workspace}}/build shell: bash run: | - pwd - find ../third-party make - name: Build (with Meson) @@ -122,38 +120,21 @@ jobs: export QT_QPA_PLATFORM=offscreen meson test - - name: Package (via CMake/CPack) - working-directory: ${{github.workspace}}/build - shell: bash - run: | - umask 022 - make package - - - name: Package (New) + # Note that, although we continue to support CMake for local builds and installs, we no longer support packaging + # with CPack/CMake. The bt build script packaging gives us better control over the packaging process. + - name: Package working-directory: ${{github.workspace}}/mbuild shell: bash run: | umask 022 ../bt package - - name: LintianAndRpmLint - continue-on-error: true - working-directory: ${{github.workspace}}/build - shell: bash - run: make package_lint - - name: Upload Linux Packages (Installers) if: ${{ success() }} uses: actions/upload-artifact@v3 with: name: brewken-${{matrix.os}} path: | - build/brewken*.rpm - build/brewken*.rpm.sha256 - build/brewken*.deb - build/brewken*.deb.sha256 - build/brewken*.tar.bz2 - build/brewken*.tar.bz2.sha256 mbuild/packages/source/brewken*.tar.xz mbuild/packages/source/brewken*.tar.xz.sha256sum mbuild/packages/linux/brewken*.deb diff --git a/.github/workflows/mac.yml b/.github/workflows/mac.yml index 4da4fffc..1ae8ceb2 100644 --- a/.github/workflows/mac.yml +++ b/.github/workflows/mac.yml @@ -117,16 +117,10 @@ jobs: export QT_QPA_PLATFORM=offscreen meson test - - name: Package (via CMake/CPack) - # Change `make package` to `make package VERBOSE=1` to get hugely detailed output - run: | - cd build - pwd - make package - pwd - tree -sh - - - name: Package (New) + # Note that, although we continue to support CMake for local builds and installs, we no longer support packaging + # with CPack/CMake -- not least because it was very hard to get things working on Mac. The bt build script + # packaging works fine and gives us better control over the packaging process. + - name: Package shell: bash run: | cd mbuild @@ -142,8 +136,6 @@ jobs: with: name: brewken-dev-mac path: | - ${{github.workspace}}/build/brewken*.dmg - ${{github.workspace}}/build/brewken*.dmg.sha256 ${{github.workspace}}/mbuild/packages/darwin/brewken*.dmg ${{github.workspace}}/mbuild/packages/darwin/brewken*.dmg.sha256sum retention-days: 7 diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index c88140e3..6203dd17 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -199,17 +199,14 @@ jobs: export QT_QPA_PLATFORM=offscreen meson test - - name: Package (via CMake/CPack) - shell: msys2 {0} - run: | - cd /C/_/build - cmake --build . --target package --verbose - # # See above for explanation of the extra things we need to do on Windows before running the bt script. Most of # that does not need doing again here, but PYTHONIOENCODING does need setting again. # - - name: Package (New) + # Note that, although we continue to support CMake for local builds and installs, we no longer support packaging + # with CPack/CMake. The bt build script packaging gives us better control over the packaging process. + # + - name: Package shell: msys2 {0} run: | cd /C/_/ @@ -234,8 +231,6 @@ jobs: with: name: brewken-dev-${{ matrix.msystem }} path: | - C:/_/build/brewken*.exe - C:/_/build/brewken*.exe.sha256 C:/_/mbuild/packages/windows/Brewken*Installer.exe C:/_/mbuild/packages/windows/Brewken*Installer.exe.sha256sum retention-days: 7 diff --git a/CMakeLists.txt b/CMakeLists.txt index edde01b3..0c52bfeb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ #---------------------------------------------------------------------------------------------------------------------- -# CMakeLists.txt is part of Brewken, and is copyright the following authors 2009-2022: +# CMakeLists.txt is part of Brewken, and is copyright the following authors 2009-2023: # • Chris Pavetto # • Dan Cavanagh # • Daniel Moreno @@ -24,6 +24,13 @@ # . #---------------------------------------------------------------------------------------------------------------------- +# ⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ +# NB: Meson and the `bt` build tool Python script are now the primary way of building and packaging the software. You +# can also still CMake to compile the product and install it locally, but we no longer support using CMake to do +# packaging. (Over time the intention is to remove packaging-specific code from this script, not least as it does +# not work properly on Mac and Windows.) +# ⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ + # # Creates a Makefile in the build directory, from where you can do builds and installs. # @@ -46,26 +53,11 @@ # * make clean - Delete compiled objects so next build starts from scratch # * sudo make install - Install locally # * make test - Run unit tests via CTest -# * make package - Makes .deb, .rpm, NSIS Installer, and .tar.bz2 binary packages. (TBD What about Mac?) -# -# On Linux, after make package, it's one of the following to install, if you want to -# install from the package rather than direct from the build tree (with sudo make install). -# (This would typically be for installing the package on a different machine then where the -# build was done.) -# $ sudo dpkg -i brewken*.deb -# $ sudo rpm -i brewken*.rpm -# -# On Mac and Windows environments, the `package` target will create an installer -# that may be executed to finish the installation. -# -# * make package_source - Makes a .tar.bz2 source package. # # Custom make targets: # * make source_doc - Makes Doxygen HTML documentation of the source in doc/html # * make install-data # * make install-runtime -# * make package_lint - Runs lintian and rpmlint on packages -# # # CMake options # * CMAKE_INSTALL_PREFIX - /usr/local by default. Set this to /usr on Debian-based systems like Ubuntu. @@ -189,13 +181,6 @@ if(UNIX AND NOT APPLE) elseif(WIN32) #============================================ Windows Install Directories =========================================== - # For some damn reason, for the NSIS installer, - # the prefix needs to be empty. Also, seems that the .exe - # needs to be in bin/. Fucking piece of shit CPack... - # Can anybody shed some light on this situation? - #set(CMAKE_INSTALL_PREFIX "") - set(CPACK_INSTALL_PREFIX "") - set(installSubDir_data "data") set(installSubDir_doc "doc") set(installSubDir_bin "bin") @@ -242,8 +227,8 @@ set(RUNTIME_INSTALL_COMPONENT "Runtime") # We use different compilers on different platforms. Where possible, we want to let CMake handle the actual compiler # settings set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -# We need C++20 for std::map::contains(), C++17 or later for nested namespaces and structured bindings, and C++11 or -# later for lambdas. +# We need C++20 for std::map::contains() and concepts, C++17 or later for nested namespaces and structured bindings, and +# C++11 or later for lambdas. set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) @@ -285,6 +270,7 @@ if(NOT ${NO_MESSING_WITH_FLAGS}) else() set(CMAKE_CXX_FLAGS_DEBUG "-Wall -g3 -no-pie -fno-pie") endif() + endif() # Speed up compilation if using gcc. @@ -293,6 +279,13 @@ if(NOT ${NO_MESSING_WITH_FLAGS}) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -pipe") endif() endif() +if(CMAKE_COMPILER_IS_GNUCXX) + # + # On older versions of GCC, it is not sufficient to specify C++20 to enable concepts, you also have to set a + # special compiler flag. + # + add_compile_options(-fconcepts) +endif() # Windows-specific compilation settings if(WIN32) diff --git a/bt b/bt index d835153f..32de8088 100755 --- a/bt +++ b/bt @@ -16,7 +16,11 @@ #----------------------------------------------------------------------------------------------------------------------- #----------------------------------------------------------------------------------------------------------------------- -# This build tool (bt) script helps with Git setup, meson build configuration and packaging. Usage is: +# This build tool (bt) script helps with Git setup, meson build configuration and packaging. You need to have Python +# 3.10 or newer installed first. On Windows you also need to have Pip installed and to do some extra manual stuff noted +# below. +# +# Usage is: # # ./bt setup Sets up Git options and configures the 'mbuild' meson build directory # @@ -27,7 +31,8 @@ # need to be for a local install). Then creates a distributable package, making use # of various build variables passed back from Meson. # -# +# NOTE: This tool, used in conjunction with Meson, is now the official way to build and package the software. We +# continue to support CMake for local compiles and installs, but not for packaging. # # .:TODO:. At some point we should be able to retire: # configure @@ -42,6 +47,37 @@ # double quotes avoids having to escape a single quote. #----------------------------------------------------------------------------------------------------------------------- +#----------------------------------------------------------------------------------------------------------------------- +# ********************************************************************************************************************** +# * +# * WINDOWS USERS PLEASE NOTE that, on Windows, we assume you are running in the MSYS2 MINGW32 environment. There are +# * also a couple of extra things you need to do before running this bt script: +# * +# * - For historical reasons, Linux and other platforms need to run both Python v2 (still used by some bits of +# * system) and Python v3 (eg that you installed yourself) so there are usually two corresponding Python +# * executables, python2 and python3. On Windows there is only whatever Python you installed and it's called +# * python.exe. To keep the shebang in the bt script working, we just make a softlink to python called python3: +# * +# * if [[ ! -f $(dirname $(which python))/python3 ]]; then ln -s $(which python) $(dirname $(which python))/python3; fi +# * +# * - Getting Unicode input/output to work is fun. We should already have a Unicode locale, but it seems we also +# * need to set PYTHONIOENCODING (see https://docs.python.org/3/using/cmdline.html#envvar-PYTHONIOENCODING, even +# * though it seems to imply you don't need to set it on recent versions of Python). +# * +# * export PYTHONIOENCODING=utf8 +# * +# * - The version of Pip we install above does not put it in the "right" place. Specifically it will not be in the +# * PATH when we run bt. The following seems to be the least hacky way around this: +# * +# * curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py +# * python get-pip.py +# * python -m pip install -U --force-reinstall pip +# * +# * See https://stackoverflow.com/questions/48087004/installing-pip-on-msys for more discussion on this. +# * +# ********************************************************************************************************************** +#----------------------------------------------------------------------------------------------------------------------- + #----------------------------------------------------------------------------------------------------------------------- # Python built-in modules we use #----------------------------------------------------------------------------------------------------------------------- @@ -1158,7 +1194,7 @@ def doPackage(): # # Debian and RPM both want the debugging information stripped from the executable. # - # .:TBD:. One day perhaps we could be friendlyi and still ship the debugging info, just in a separate .dbg + # .:TBD:. One day perhaps we could be friendly and still ship the debugging info, just in a separate .dbg # file. The procedure to do this is described in the 'only-keep-debug' section of `man objcopy`. However, we # need to work out where to put the .dbg file so that it remains usable but lintian does not complain about it. # diff --git a/meson.build b/meson.build index 6a44b3ff..f5dea15a 100644 --- a/meson.build +++ b/meson.build @@ -16,9 +16,8 @@ #----------------------------------------------------------------------------------------------------------------------- # -# ⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ -# ⭐⭐⭐ THIS IS EXPERIMENTAL - YOU CAN ALSO STILL USE TRIED-AND-TESTED CMAKE TO BUILD THE PRODUCT -# ⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ +# NB: This is now the primary way of building and packaging the software. You can also still CMake to compile the +# product and install it locally, but we no longer support using CMake to do packaging. # # STEP 1: Ensure Python is installed: # ----------------------------------- @@ -588,13 +587,10 @@ commonSourceFiles = files([ 'src/BrewDayFormatter.cpp', 'src/BrewDayScrollWidget.cpp', 'src/BrewNoteWidget.cpp', - 'src/BtAmountEdit.cpp', 'src/BtColor.cpp', 'src/BtDatePopup.cpp', 'src/BtFieldType.cpp', 'src/BtFolder.cpp', - 'src/BtLabel.cpp', - 'src/BtLineEdit.cpp', 'src/BtSplashScreen.cpp', 'src/BtTabWidget.cpp', 'src/BtTextEdit.cpp', @@ -603,28 +599,36 @@ commonSourceFiles = files([ 'src/BtTreeModel.cpp', 'src/BtTreeView.cpp', 'src/ConverterTool.cpp', - 'src/CustomComboBox.cpp', 'src/database/BtSqlQuery.cpp', 'src/database/Database.cpp', 'src/database/DatabaseSchemaHelper.cpp', 'src/database/DbTransaction.cpp', 'src/database/ObjectStore.cpp', 'src/database/ObjectStoreTyped.cpp', + 'src/editors/EquipmentEditor.cpp', + 'src/editors/FermentableEditor.cpp', + 'src/editors/HopEditor.cpp', + 'src/editors/MashEditor.cpp', + 'src/editors/MashStepEditor.cpp', + 'src/editors/MiscEditor.cpp', + 'src/editors/NamedMashEditor.cpp', + 'src/editors/StyleEditor.cpp', + 'src/editors/WaterEditor.cpp', + 'src/editors/YeastEditor.cpp', 'src/EquipmentButton.cpp', - 'src/EquipmentEditor.cpp', 'src/EquipmentListModel.cpp', - 'src/FermentableDialog.cpp', - 'src/FermentableEditor.cpp', 'src/FermentableSortFilterProxyModel.cpp', 'src/HeatCalculations.cpp', 'src/HelpDialog.cpp', - 'src/HopDialog.cpp', - 'src/HopEditor.cpp', 'src/HopSortFilterProxyModel.cpp', 'src/Html.cpp', 'src/HydrometerTool.cpp', 'src/IbuGuSlider.cpp', 'src/ImportExport.cpp', + 'src/ingredientDialogs/FermentableDialog.cpp', + 'src/ingredientDialogs/HopDialog.cpp', + 'src/ingredientDialogs/MiscDialog.cpp', + 'src/ingredientDialogs/YeastDialog.cpp', 'src/InstructionWidget.cpp', 'src/InventoryFormatter.cpp', 'src/json/BeerJson.cpp', @@ -639,11 +643,8 @@ commonSourceFiles = files([ 'src/Logging.cpp', 'src/MainWindow.cpp', 'src/MashButton.cpp', - 'src/MashComboBox.cpp', 'src/MashDesigner.cpp', - 'src/MashEditor.cpp', 'src/MashListModel.cpp', - 'src/MashStepEditor.cpp', 'src/MashStepTableWidget.cpp', 'src/MashWizard.cpp', 'src/matrix.cpp', @@ -657,8 +658,6 @@ commonSourceFiles = files([ 'src/measurement/SystemOfMeasurement.cpp', 'src/measurement/Unit.cpp', 'src/measurement/UnitSystem.cpp', - 'src/MiscDialog.cpp', - 'src/MiscEditor.cpp', 'src/MiscSortFilterProxyModel.cpp', 'src/model/BrewNote.cpp', 'src/model/Equipment.cpp', @@ -678,7 +677,6 @@ commonSourceFiles = files([ 'src/model/Water.cpp', 'src/model/Yeast.cpp', 'src/NamedEntitySortProxyModel.cpp', - 'src/NamedMashEditor.cpp', 'src/OgAdjuster.cpp', 'src/OptionDialog.cpp', 'src/PersistentSettings.cpp', @@ -692,10 +690,8 @@ commonSourceFiles = files([ 'src/RecipeFormatter.cpp', 'src/RefractoDialog.cpp', 'src/ScaleRecipeTool.cpp', - 'src/SimpleUndoableUpdate.cpp', 'src/StrikeWaterDialog.cpp', 'src/StyleButton.cpp', - 'src/StyleEditor.cpp', 'src/StyleListModel.cpp', 'src/StyleRangeWidget.cpp', 'src/StyleSortFilterProxyModel.cpp', @@ -711,25 +707,34 @@ commonSourceFiles = files([ 'src/TimerListDialog.cpp', 'src/TimerMainDialog.cpp', 'src/TimerWidget.cpp', - 'src/UiAmountWithUnits.cpp', + 'src/undoRedo/SimpleUndoableUpdate.cpp', 'src/utils/BtException.cpp', 'src/utils/BtStringConst.cpp', 'src/utils/BtStringStream.cpp', 'src/utils/EnumStringMapping.cpp', 'src/utils/ImportRecordCount.cpp', + 'src/utils/MetaTypes.cpp', + 'src/utils/OptionalHelpers.cpp', 'src/utils/OStreamWriterForQFile.cpp', 'src/utils/TimerUtils.cpp', 'src/utils/TypeLookup.cpp', 'src/WaterButton.cpp', 'src/WaterDialog.cpp', - 'src/WaterEditor.cpp', 'src/WaterListModel.cpp', 'src/WaterSortFilterProxyModel.cpp', - 'src/WaterTableWidget.cpp', 'src/widgets/Animator.cpp', - 'src/widgets/BtAmountDigitWidget.cpp', - 'src/widgets/BtDigitWidget.cpp', + 'src/widgets/BtBoolComboBox.cpp', + 'src/widgets/BtComboBox.cpp', + 'src/widgets/CustomComboBox.cpp', + 'src/widgets/MashComboBox.cpp', 'src/widgets/SelectionControl.cpp', + 'src/widgets/SmartAmounts.cpp', + 'src/widgets/SmartAmountSettings.cpp', + 'src/widgets/SmartCheckBox.cpp', + 'src/widgets/SmartDigitWidget.cpp', + 'src/widgets/SmartField.cpp', + 'src/widgets/SmartLabel.cpp', + 'src/widgets/SmartLineEdit.cpp', 'src/widgets/ToggleSwitch.cpp', 'src/widgets/UnitAndScalePopUpMenu.cpp', 'src/xml/BeerXml.cpp', @@ -740,8 +745,6 @@ commonSourceFiles = files([ 'src/xml/XmlMashStepRecord.cpp', 'src/xml/XmlRecipeRecord.cpp', 'src/xml/XmlRecord.cpp', - 'src/YeastDialog.cpp', - 'src/YeastEditor.cpp', 'src/YeastSortFilterProxyModel.cpp', ]) @@ -769,11 +772,8 @@ mocHeaders = files([ 'src/BrewDayFormatter.h', 'src/BrewDayScrollWidget.h', 'src/BrewNoteWidget.h', - 'src/BtAmountEdit.h', 'src/BtDatePopup.h', 'src/BtFolder.h', - 'src/BtLabel.h', - 'src/BtLineEdit.h', 'src/BtSplashScreen.h', 'src/BtTabWidget.h', 'src/BtTextEdit.h', @@ -781,32 +781,35 @@ mocHeaders = files([ 'src/BtTreeModel.h', 'src/BtTreeView.h', 'src/ConverterTool.h', - 'src/CustomComboBox.h', 'src/database/ObjectStore.h', + 'src/editors/EquipmentEditor.h', + 'src/editors/FermentableEditor.h', + 'src/editors/HopEditor.h', + 'src/editors/MashEditor.h', + 'src/editors/MashStepEditor.h', + 'src/editors/MiscEditor.h', + 'src/editors/NamedMashEditor.h', + 'src/editors/StyleEditor.h', + 'src/editors/WaterEditor.h', + 'src/editors/YeastEditor.h', 'src/EquipmentButton.h', - 'src/EquipmentEditor.h', 'src/EquipmentListModel.h', - 'src/FermentableDialog.h', - 'src/FermentableEditor.h', 'src/FermentableSortFilterProxyModel.h', 'src/HelpDialog.h', - 'src/HopDialog.h', - 'src/HopEditor.h', 'src/HopSortFilterProxyModel.h', 'src/HydrometerTool.h', 'src/IbuGuSlider.h', + 'src/ingredientDialogs/FermentableDialog.h', + 'src/ingredientDialogs/HopDialog.h', + 'src/ingredientDialogs/MiscDialog.h', + 'src/ingredientDialogs/YeastDialog.h', 'src/InstructionWidget.h', 'src/MainWindow.h', 'src/MashButton.h', - 'src/MashComboBox.h', 'src/MashDesigner.h', - 'src/MashEditor.h', 'src/MashListModel.h', - 'src/MashStepEditor.h', 'src/MashStepTableWidget.h', 'src/MashWizard.h', - 'src/MiscDialog.h', - 'src/MiscEditor.h', 'src/MiscSortFilterProxyModel.h', 'src/model/BrewNote.h', 'src/model/Equipment.h', @@ -825,7 +828,6 @@ mocHeaders = files([ 'src/model/Water.h', 'src/model/Yeast.h', 'src/NamedEntitySortProxyModel.h', - 'src/NamedMashEditor.h', 'src/OgAdjuster.h', 'src/OptionDialog.h', 'src/PitchDialog.h', @@ -836,10 +838,8 @@ mocHeaders = files([ 'src/RecipeFormatter.h', 'src/RefractoDialog.h', 'src/ScaleRecipeTool.h', - 'src/SimpleUndoableUpdate.h', 'src/StrikeWaterDialog.h', 'src/StyleButton.h', - 'src/StyleEditor.h', 'src/StyleListModel.h', 'src/StyleRangeWidget.h', 'src/StyleSortFilterProxyModel.h', @@ -856,17 +856,19 @@ mocHeaders = files([ 'src/TimerWidget.h', 'src/WaterButton.h', 'src/WaterDialog.h', - 'src/WaterEditor.h', 'src/WaterListModel.h', 'src/WaterSortFilterProxyModel.h', - 'src/WaterTableWidget.h', 'src/widgets/Animator.h', - 'src/widgets/BtAmountDigitWidget.h', - 'src/widgets/BtDigitWidget.h', + 'src/widgets/BtBoolComboBox.h', + 'src/widgets/BtComboBox.h', + 'src/widgets/CustomComboBox.h', + 'src/widgets/MashComboBox.h', 'src/widgets/SelectionControl.h', + 'src/widgets/SmartCheckBox.h', + 'src/widgets/SmartDigitWidget.h', + 'src/widgets/SmartLabel.h', + 'src/widgets/SmartLineEdit.h', 'src/widgets/ToggleSwitch.h', - 'src/YeastDialog.h', - 'src/YeastEditor.h', 'src/YeastSortFilterProxyModel.h', ]) @@ -1413,6 +1415,9 @@ if compiler.get_id() == 'gcc' # However, even this is not sufficient(!). So, for the moment, we suppress the rpmlint error (see # packaging/rpmLintFilters.toml). # + # -fconcepts Is needed on older versions of GCC to enable C++20 concepts (because specifying C++20 was not enough + # for some reason). + # # The following are, according to some comments at # https://stackoverflow.com/questions/52583544/boost-stack-trace-not-showing-function-names-and-line-numbers, needed # for Boost stacktrace to work properly: @@ -1428,6 +1433,7 @@ if compiler.get_id() == 'gcc' add_global_arguments(['-g3', '-O2', '-z', 'noexecstack', # NB Not '-z noexecstack' as otherwise will be passed to gcc in quotes! + '-fconcepts' ], language : 'cpp') if host_machine.system() == 'windows' add_global_arguments (['-no-pie', '-fno-pie'], language : 'cpp') diff --git a/src/AlcoholTool.cpp b/src/AlcoholTool.cpp index fa4e7242..f94e72e0 100644 --- a/src/AlcoholTool.cpp +++ b/src/AlcoholTool.cpp @@ -27,8 +27,8 @@ #include #include "Algorithms.h" -#include "BtAmountEdit.h" -#include "BtLineEdit.h" +#include "widgets/SmartLabel.h" +#include "widgets/SmartLineEdit.h" #include "Localization.h" #include "PersistentSettings.h" #include "measurement/SystemOfMeasurement.h" @@ -49,27 +49,35 @@ class AlcoholTool::impl { */ impl(AlcoholTool & self) : self {self}, - label_reading {new QLabel (&self)}, - label_temperature {new QLabel (&self)}, - label_corrected {new QLabel (&self)}, - enableAdvancedInputs {new ToggleSwitch (&self)}, - label_og {new QLabel (&self)}, - input_og {new BtDensityEdit (&self)}, - input_og_temperature {new BtTemperatureEdit(&self)}, - corrected_og {new QLabel (&self)}, - label_fg {new QLabel (&self)}, - input_fg {new BtDensityEdit (&self)}, - input_fg_temperature {new BtTemperatureEdit(&self)}, - corrected_fg {new QLabel (&self)}, - label_calibration_temperature{new QLabel (&self)}, - input_calibration_temperature{new BtTemperatureEdit(&self)}, - label_result {new QLabel (&self)}, - output_result {new QLabel (&self)}, - gridLayout {new QGridLayout (&self)} { + label_reading {new QLabel (&self)}, + label_temperature {new SmartLabel (&self)}, + label_corrected {new QLabel (&self)}, + enableAdvancedInputs {new ToggleSwitch (&self)}, + label_og {new SmartLabel (&self)}, + input_og {new SmartLineEdit(&self)}, + input_og_temperature {new SmartLineEdit(&self)}, + corrected_og {new QLabel (&self)}, + label_fg {new SmartLabel (&self)}, + input_fg {new SmartLineEdit(&self)}, + input_fg_temperature {new SmartLineEdit(&self)}, + corrected_fg {new QLabel (&self)}, + label_calibration_temperature{new SmartLabel (&self)}, + input_calibration_temperature{new SmartLineEdit(&self)}, + label_result {new QLabel (&self)}, + output_result {new QLabel (&self)}, + gridLayout {new QGridLayout (&self)} { + + SMART_FIELD_INIT_FS(AlcoholTool, label_og , input_og , double, Measurement::PhysicalQuantity::Density ); + SMART_FIELD_INIT_FS(AlcoholTool, label_fg , input_fg , double, Measurement::PhysicalQuantity::Density ); + SMART_FIELD_INIT_FS(AlcoholTool, label_temperature , input_og_temperature , double, Measurement::PhysicalQuantity::Temperature); + SMART_FIELD_INIT_FS(AlcoholTool, label_temperature , input_fg_temperature , double, Measurement::PhysicalQuantity::Temperature); + SMART_FIELD_INIT_FS(AlcoholTool, label_calibration_temperature, input_calibration_temperature, double, Measurement::PhysicalQuantity::Temperature); + this->restoreSettings(); this->enableAdvancedInputs->setFont(QFont("Roboto medium", 13)); this->output_result->setText("%"); this->doLayout(); + this->connectSignals(); return; } @@ -87,10 +95,10 @@ class AlcoholTool::impl { void doLayout() { this->input_og->setMinimumSize(QSize(80, 0)); - this->input_og->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); +/// this->input_og->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); this->input_fg->setMinimumSize(QSize(80, 0)); - this->input_fg->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); +/// this->input_fg->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); this->label_result->setObjectName(QStringLiteral("label_results")); this->label_result->setContextMenuPolicy(Qt::CustomContextMenu); @@ -127,12 +135,12 @@ class AlcoholTool::impl { void showOrHideAdvancedControls() { bool visible = this->enableAdvancedInputs->isChecked(); - this->label_temperature->setVisible(visible); - this->label_corrected->setVisible(visible); - this->input_og_temperature->setVisible(visible); - this->corrected_og->setVisible(visible); - this->input_fg_temperature->setVisible(visible); - this->corrected_fg->setVisible(visible); + this->label_temperature ->setVisible(visible); + this->label_corrected ->setVisible(visible); + this->input_og_temperature ->setVisible(visible); + this->corrected_og ->setVisible(visible); + this->input_fg_temperature ->setVisible(visible); + this->corrected_fg ->setVisible(visible); this->label_calibration_temperature->setVisible(visible); this->input_calibration_temperature->setVisible(visible); @@ -189,13 +197,11 @@ class AlcoholTool::impl { void connectSignals() { // If every input field triggers recalculation on modification then we don't need a "Convert" button - for (BtLineEdit const * ii : std::initializer_list{this->input_og, - this->input_fg, - this->input_og_temperature, - this->input_fg_temperature, - this->input_calibration_temperature}) { - connect(ii, &BtLineEdit::textModified, &self, &AlcoholTool::calculate); - } + connect(this->input_og , &SmartLineEdit::textModified, &self, &AlcoholTool::calculate); + connect(this->input_fg , &SmartLineEdit::textModified, &self, &AlcoholTool::calculate); + connect(this->input_og_temperature , &SmartLineEdit::textModified, &self, &AlcoholTool::calculate); + connect(this->input_fg_temperature , &SmartLineEdit::textModified, &self, &AlcoholTool::calculate); + connect(this->input_calibration_temperature, &SmartLineEdit::textModified, &self, &AlcoholTool::calculate); // This will also make the recalculation call after toggling the visibility of advanced controls connect(this->enableAdvancedInputs, &QAbstractButton::clicked, &self, &AlcoholTool::toggleAdvancedControls); @@ -204,13 +210,13 @@ class AlcoholTool::impl { void retranslateUi() { self.setWindowTitle(tr("Alcohol Tool")); - this->label_og->setText(tr("Original Gravity (OG)")); - this->label_result->setText(tr("ABV")); - this->label_fg->setText(tr("Final Gravity (FG)")); - this->label_reading->setText(tr("Reading")); - this->label_temperature->setText(tr("Temperature")); - this->label_corrected->setText(tr("Corrected Reading")); - this->enableAdvancedInputs->setText(tr("Advanced Mode")); + this->label_og ->setText(tr("Original Gravity (OG)")); + this->label_result ->setText(tr("ABV")); + this->label_fg ->setText(tr("Final Gravity (FG)")); + this->label_reading ->setText(tr("Reading")); + this->label_temperature ->setText(tr("Temperature")); + this->label_corrected ->setText(tr("Corrected Reading")); + this->enableAdvancedInputs ->setText(tr("Advanced Mode")); this->label_calibration_temperature->setText(tr("Hydrometer Calibration Temperature")); #ifndef QT_NO_TOOLTIP @@ -238,10 +244,10 @@ class AlcoholTool::impl { // Hydrometer calibration temperature -- default is 20°C, or 68°F in the old money. // Working out which units to use is already solved elsewhere in the code base, but you just have to be careful - // not to do the conversion twice (ie 20°C -> 68°F ... 68°C -> 154°F) as both BtLineEdit::setText() and - // Measurement::amountDisplay() take SI unit and convert them to whatever the user has chosen to display. So you just - // need BtLineEdit::setText(). - this->input_calibration_temperature->setText( + // not to do the conversion twice (ie 20°C -> 68°F ... 68°C -> 154°F) as both SmartLineEdit::setAmount() and + // Measurement::amountDisplay() take SI unit and convert them to whatever the user has chosen to display. So you + // just need SmartLineEdit::setAmount(). + this->input_calibration_temperature->setAmount( PersistentSettings::value(hydrometerCalibrationTemperatureInC, 20.0, PersistentSettings::Sections::alcoholTool).toDouble() @@ -261,25 +267,25 @@ class AlcoholTool::impl { } // Member variables for impl - AlcoholTool & self; - QLabel * label_reading; - QLabel * label_temperature; - QLabel * label_corrected; - ToggleSwitch * enableAdvancedInputs; - QLabel * label_og; - BtDensityEdit * input_og; - BtTemperatureEdit * input_og_temperature; - QLabel * corrected_og; - QLabel * label_fg; - BtDensityEdit * input_fg; - BtTemperatureEdit * input_fg_temperature; - QLabel * corrected_fg; - QLabel * label_calibration_temperature; - BtTemperatureEdit * input_calibration_temperature; - QPushButton * pushButton_convert; - QLabel * label_result; - QLabel * output_result; - QGridLayout * gridLayout; + AlcoholTool & self; + QLabel * label_reading; + SmartLabel * label_temperature; + QLabel * label_corrected; + ToggleSwitch * enableAdvancedInputs; + SmartLabel * label_og; + SmartLineEdit * input_og; + SmartLineEdit * input_og_temperature; + QLabel * corrected_og; + SmartLabel * label_fg; + SmartLineEdit * input_fg; + SmartLineEdit * input_fg_temperature; + QLabel * corrected_fg; + SmartLabel * label_calibration_temperature; + SmartLineEdit * input_calibration_temperature; + QPushButton * pushButton_convert; + QLabel * label_result; + QLabel * output_result; + QGridLayout * gridLayout; }; AlcoholTool::AlcoholTool(QWidget* parent) : QDialog(parent), diff --git a/src/BrewDayFormatter.cpp b/src/BrewDayFormatter.cpp index c3f9fcd1..03addb37 100644 --- a/src/BrewDayFormatter.cpp +++ b/src/BrewDayFormatter.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * BrewDayFormatter.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * BrewDayFormatter.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Jeff Bailey * • Mattias Måhl * • Matt Young @@ -74,9 +74,7 @@ QString BrewDayFormatter::buildTitleHtml(bool includeImage) { .arg(tr("Boil Time")) .arg( recObs->equipment() ? Measurement::displayAmount(Measurement::Amount{recObs->equipment()->boilTime_min(), - Measurement::Units::minutes}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Equipment::boilTime_min) : "unknown" + Measurement::Units::minutes}) : "unknown" ) .arg(tr("Efficiency")) .arg(Measurement::displayQuantity(recObs->efficiency_pct(), 0)); @@ -84,38 +82,23 @@ QString BrewDayFormatter::buildTitleHtml(bool includeImage) { // third row: pre-Boil Volume and Preboil Gravity body += QString("%1%2%3%4") .arg(tr("Boil Volume")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilVolume_l, - 2)) + .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilVolume_l(), Measurement::Units::liters}, 2)) .arg(tr("Preboil Gravity")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilGrav(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilGrav, - 3)); + .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilGrav(), Measurement::Units::specificGravity}, 3)); // fourth row: Final volume and starting gravity body += QString("%1%2%3%4") .arg(tr("Final Volume")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->finalVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::finalVolume_l, - 2)) + .arg(Measurement::displayAmount(Measurement::Amount{recObs->finalVolume_l(), Measurement::Units::liters}, 2)) .arg(tr("Starting Gravity")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->og(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::og, - 3)); + .arg(Measurement::displayAmount(Measurement::Amount{recObs->og(), Measurement::Units::specificGravity}, 3)); // fifth row: IBU and Final gravity body += QString("%1%2%3%4") .arg(tr("IBU")) .arg(Measurement::displayQuantity(recObs->IBU(), 1)) .arg(tr("Final Gravity")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->fg(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::fg, - 3)); + .arg(Measurement::displayAmount(Measurement::Amount{recObs->fg(), Measurement::Units::specificGravity}, 3)); // sixth row: ABV and estimate calories bool metricVolume = @@ -153,9 +136,7 @@ QList BrewDayFormatter::buildTitleList() { row.append(tr("Boil Time")); row.append( recObs->equipment() ? Measurement::displayAmount(Measurement::Amount{recObs->equipment()->boilTime_min(), - Measurement::Units::minutes}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Equipment::boilTime_min) : "unknown" + Measurement::Units::minutes}) : "unknown" ); row.append(tr("Efficiency")); row.append(Measurement::displayQuantity(recObs->efficiency_pct(), 0)); @@ -164,15 +145,9 @@ QList BrewDayFormatter::buildTitleList() { // third row: pre-Boil Volume and Preboil Gravity row.append(tr("Boil Volume")); - row.append(Measurement::displayAmount(Measurement::Amount{recObs->boilVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilVolume_l, - 2)); + row.append(Measurement::displayAmount(Measurement::Amount{recObs->boilVolume_l(), Measurement::Units::liters}, 2)); row.append(tr("Preboil Gravity")); - row.append(Measurement::displayAmount(Measurement::Amount{recObs->boilGrav(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilGrav, - 3)); + row.append(Measurement::displayAmount(Measurement::Amount{recObs->boilGrav(), Measurement::Units::specificGravity}, 3)); ret.append(row); row.clear(); ret.append(row); @@ -180,15 +155,9 @@ QList BrewDayFormatter::buildTitleList() { // fourth row: Final volume and starting gravity row.append(tr("Final Volume")); - row.append(Measurement::displayAmount(Measurement::Amount{recObs->finalVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::finalVolume_l, - 2)); + row.append(Measurement::displayAmount(Measurement::Amount{recObs->finalVolume_l(), Measurement::Units::liters}, 2)); row.append(tr("Starting Gravity")); - row.append(Measurement::displayAmount(Measurement::Amount{recObs->og(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::og, - 3)); + row.append(Measurement::displayAmount(Measurement::Amount{recObs->og(), Measurement::Units::specificGravity}, 3)); ret.append(row); row.clear(); @@ -196,10 +165,7 @@ QList BrewDayFormatter::buildTitleList() { row.append(tr("IBU")); row.append(Measurement::displayQuantity(recObs->IBU(), 1)); row.append(tr("Final Gravity")); - row.append(Measurement::displayAmount(Measurement::Amount{recObs->fg(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::fg, - 3)); + row.append(Measurement::displayAmount(Measurement::Amount{recObs->fg(), Measurement::Units::specificGravity}, 3)); ret.append(row); row.clear(); diff --git a/src/BrewDayScrollWidget.cpp b/src/BrewDayScrollWidget.cpp index 00b59ab5..bd8b75cf 100644 --- a/src/BrewDayScrollWidget.cpp +++ b/src/BrewDayScrollWidget.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * BrewDayScrollWidget.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * BrewDayScrollWidget.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Carles Muñoz Gorriz * • Daniel Pettersson @@ -54,9 +54,7 @@ namespace { return "unknown"; } - return Measurement::displayAmount(Measurement::Amount{equipment->boilTime_min(), Measurement::Units::minutes}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilTime_min); + return Measurement::displayAmount(Measurement::Amount{equipment->boilTime_min(), Measurement::Units::minutes}); } } @@ -66,14 +64,13 @@ BrewDayScrollWidget::BrewDayScrollWidget(QWidget* parent) : QWidget{parent}, this->setupUi(this); this->setObjectName("BrewDayScrollWidget"); - connect(listWidget, &QListWidget::currentRowChanged, this, &BrewDayScrollWidget::showInstruction ); - connect(btTextEdit, SIGNAL(textModified()), this, SLOT(saveInstruction()) ); -// connect(btTextEdit, &BtLineEdit::textModified, this, &BrewDayScrollWidget::saveInstruction ); - connect(pushButton_insert, &QAbstractButton::clicked, this, &BrewDayScrollWidget::insertInstruction ); - connect(pushButton_remove, &QAbstractButton::clicked, this, &BrewDayScrollWidget::removeSelectedInstruction); - connect(pushButton_up, &QAbstractButton::clicked, this, &BrewDayScrollWidget::pushInstructionUp ); - connect(pushButton_down, &QAbstractButton::clicked, this, &BrewDayScrollWidget::pushInstructionDown ); - connect(pushButton_generateInstructions, &QAbstractButton::clicked, this, &BrewDayScrollWidget::generateInstructions ); + connect(this->listWidget, &QListWidget::currentRowChanged, this, &BrewDayScrollWidget::showInstruction ); + connect(this->btTextEdit, &BtTextEdit::textModified, this, &BrewDayScrollWidget::saveInstruction ); + connect(this->pushButton_insert, &QAbstractButton::clicked, this, &BrewDayScrollWidget::insertInstruction ); + connect(this->pushButton_remove, &QAbstractButton::clicked, this, &BrewDayScrollWidget::removeSelectedInstruction); + connect(this->pushButton_up, &QAbstractButton::clicked, this, &BrewDayScrollWidget::pushInstructionUp ); + connect(this->pushButton_down, &QAbstractButton::clicked, this, &BrewDayScrollWidget::pushInstructionDown ); + connect(this->pushButton_generateInstructions, &QAbstractButton::clicked, this, &BrewDayScrollWidget::generateInstructions ); return; } @@ -82,6 +79,7 @@ BrewDayScrollWidget::~BrewDayScrollWidget() = default; void BrewDayScrollWidget::saveInstruction() { this->recObs->instructions()[ listWidget->currentRow() ]->setDirections( btTextEdit->toPlainText() ); + return; } void BrewDayScrollWidget::showInstruction(int insNdx) { @@ -273,7 +271,7 @@ void BrewDayScrollWidget::insertInstruction() { // After updating the model, this is the simplest way to update the display this->setRecipe(this->recObs); - listWidget->setCurrentRow(pos-1); + listWidget->setCurrentRow(pos - 1); return; } @@ -316,7 +314,7 @@ void BrewDayScrollWidget::showChanges() { return; } - repopulateListWidget(); + this->repopulateListWidget(); return; } @@ -376,38 +374,23 @@ QString BrewDayScrollWidget::buildTitleTable(bool includeImage) { // third row: pre-Boil Volume and Preboil Gravity body += QString("%1%2%3%4") .arg(tr("Boil Volume")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilVolume_l, - 2)) + .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilVolume_l(), Measurement::Units::liters}, 2)) .arg(tr("Preboil Gravity")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilGrav(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::og, - 3)); + .arg(Measurement::displayAmount(Measurement::Amount{recObs->boilGrav(), Measurement::Units::specificGravity}, 3)); // fourth row: Final volume and starting gravity body += QString("%1%2%3%4") .arg(tr("Final Volume")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->finalVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::finalVolume_l, - 2)) + .arg(Measurement::displayAmount(Measurement::Amount{recObs->finalVolume_l(), Measurement::Units::liters}, 2)) .arg(tr("Starting Gravity")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->og(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::og, - 3)); + .arg(Measurement::displayAmount(Measurement::Amount{recObs->og(), Measurement::Units::specificGravity}, 3)); // fifth row: IBU and Final gravity body += QString("%1%2%3%4") .arg(tr("IBU")) .arg( Measurement::displayQuantity(recObs->IBU(), 1)) .arg(tr("Final Gravity")) - .arg(Measurement::displayAmount(Measurement::Amount{recObs->fg(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::fg, - 3)); + .arg(Measurement::displayAmount(Measurement::Amount{recObs->fg(), Measurement::Units::specificGravity}, 3)); // sixth row: ABV and estimate calories bool metricVolume = ( diff --git a/src/BrewNoteWidget.cpp b/src/BrewNoteWidget.cpp index 478f110c..791d3b83 100644 --- a/src/BrewNoteWidget.cpp +++ b/src/BrewNoteWidget.cpp @@ -38,29 +38,46 @@ BrewNoteWidget::BrewNoteWidget(QWidget *parent) : QWidget(parent) { bNoteObs = 0; setObjectName("BrewNoteWidget"); - connect(lineEdit_SG, &BtLineEdit::textModified, this, &BrewNoteWidget::updateSG); - connect(lineEdit_volIntoBK, &BtLineEdit::textModified, this, &BrewNoteWidget::updateVolumeIntoBK_l); - connect(lineEdit_strikeTemp, &BtLineEdit::textModified, this, &BrewNoteWidget::updateStrikeTemp_c); - connect(lineEdit_mashFinTemp, &BtLineEdit::textModified, this, &BrewNoteWidget::updateMashFinTemp_c); - - connect(lineEdit_OG, &BtLineEdit::textModified, this, &BrewNoteWidget::updateOG); - connect(lineEdit_postBoilVol, &BtLineEdit::textModified, this, &BrewNoteWidget::updatePostBoilVolume_l); - connect(lineEdit_volIntoFerm, &BtLineEdit::textModified, this, &BrewNoteWidget::updateVolumeIntoFerm_l); - connect(lineEdit_pitchTemp, &BtLineEdit::textModified, this, &BrewNoteWidget::updatePitchTemp_c); - - connect(lineEdit_FG, &BtLineEdit::textModified, this, &BrewNoteWidget::updateFG); - connect(lineEdit_finalVol, &BtLineEdit::textModified, this, &BrewNoteWidget::updateFinalVolume_l); - connect(lineEdit_fermentDate, &QDateTimeEdit::dateChanged, this, &BrewNoteWidget::updateFermentDate); - - connect(btTextEdit_brewNotes, &BtTextEdit::textModified, this, &BrewNoteWidget::updateNotes); + SMART_FIELD_INIT(BrewNoteWidget, label_Fg , lineEdit_Fg , BrewNote, PropertyNames::BrewNote::fg ); + SMART_FIELD_INIT(BrewNoteWidget, label_Og , lineEdit_Og , BrewNote, PropertyNames::BrewNote::og ); + SMART_FIELD_INIT(BrewNoteWidget, label_Sg , lineEdit_Sg , BrewNote, PropertyNames::BrewNote::sg ); + SMART_FIELD_INIT(BrewNoteWidget, label_mashFinTemp, lineEdit_mashFinTemp , BrewNote, PropertyNames::BrewNote::mashFinTemp_c ); + SMART_FIELD_INIT(BrewNoteWidget, label_pitchTemp , lineEdit_pitchTemp , BrewNote, PropertyNames::BrewNote::pitchTemp_c ); + SMART_FIELD_INIT(BrewNoteWidget, label_strikeTemp , lineEdit_strikeTemp , BrewNote, PropertyNames::BrewNote::strikeTemp_c ); + SMART_FIELD_INIT(BrewNoteWidget, label_finalVolume, lineEdit_finalVolume , BrewNote, PropertyNames::BrewNote::finalVolume_l ); + SMART_FIELD_INIT(BrewNoteWidget, label_postBoilVol, lineEdit_postBoilVol , BrewNote, PropertyNames::BrewNote::postBoilVolume_l); + SMART_FIELD_INIT(BrewNoteWidget, label_volIntoBk , lineEdit_volIntoBk , BrewNote, PropertyNames::BrewNote::volumeIntoBK_l ); + SMART_FIELD_INIT(BrewNoteWidget, label_volIntoFerm, lineEdit_volIntoFerm , BrewNote, PropertyNames::BrewNote::volumeIntoFerm_l); +// SMART_FIELD_INIT(BrewNoteWidget, label_fermentDate , lineEdit_fermentDate , BrewNote, PropertyNames::BrewNote::fermentDate ); No specialisation for QDateTimeEdit + SMART_FIELD_INIT(BrewNoteWidget, label_projectedOg , lcdnumber_projectedOG , BrewNote, PropertyNames::BrewNote::projOg ); + SMART_FIELD_INIT(BrewNoteWidget, label_effInfoBk , lcdnumber_effBK , BrewNote, PropertyNames::BrewNote::effIntoBK_pct , 2); + SMART_FIELD_INIT(BrewNoteWidget, label_brewHouseEff , lcdnumber_brewhouseEff, BrewNote, PropertyNames::BrewNote::brewhouseEff_pct, 2); + SMART_FIELD_INIT(BrewNoteWidget, label_projectedAbv , lcdnumber_projABV , BrewNote, PropertyNames::BrewNote::projABV_pct , 2); + SMART_FIELD_INIT(BrewNoteWidget, label_Abv , lcdnumber_abv , BrewNote, PropertyNames::BrewNote::abv , 2); + SMART_FIELD_INIT(BrewNoteWidget, label_yeastProjAtten, lcdnumber_projAtten , BrewNote, PropertyNames::BrewNote::projAtten , 2); + SMART_FIELD_INIT(BrewNoteWidget, label_yeastAtten , lcdnumber_atten , BrewNote, PropertyNames::BrewNote::attenuation , 2); + + + connect(this->lineEdit_Sg, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateSG ); + connect(this->lineEdit_volIntoBk, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateVolumeIntoBK_l ); + connect(this->lineEdit_strikeTemp, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateStrikeTemp_c ); + connect(this->lineEdit_mashFinTemp, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateMashFinTemp_c ); + connect(this->lineEdit_Og, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateOG ); + connect(this->lineEdit_postBoilVol, &SmartLineEdit::textModified, this, &BrewNoteWidget::updatePostBoilVolume_l); + connect(this->lineEdit_volIntoFerm, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateVolumeIntoFerm_l); + connect(this->lineEdit_pitchTemp, &SmartLineEdit::textModified, this, &BrewNoteWidget::updatePitchTemp_c ); + connect(this->lineEdit_Fg, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateFG ); + connect(this->lineEdit_finalVolume, &SmartLineEdit::textModified, this, &BrewNoteWidget::updateFinalVolume_l ); + connect(this->lineEdit_fermentDate, &QDateTimeEdit::dateChanged, this, &BrewNoteWidget::updateFermentDate ); + connect(this->btTextEdit_brewNotes, &BtTextEdit::textModified, this, &BrewNoteWidget::updateNotes ); // A few labels on this page need special handling, so I connect them here // instead of how we would normally do this. - connect(btLabel_projectedOg, &BtLabel::changedSystemOfMeasurementOrScale, this, &BrewNoteWidget::updateProjOg); -/// connect(btLabel_fermentDate, &BtLabel::changedSystemOfMeasurementOrScale, this, &BrewNoteWidget::updateDateFormat); + connect(this->label_projectedOg, &SmartLabel::changedSystemOfMeasurementOrScale, this, &BrewNoteWidget::updateProjOg); // I think this might work updateDateFormat(); + return; } BrewNoteWidget::~BrewNoteWidget() = default; @@ -80,26 +97,23 @@ void BrewNoteWidget::updateDateFormat() { void BrewNoteWidget::updateProjOg() { - // Density UnitSystems only have one scale, so we don't bother looking up UnitSystem::RelativeScale - auto forcedSystemOfMeasurement = - Measurement::getForcedSystemOfMeasurementForField(*PropertyNames::BrewNote::projOg, - *PersistentSettings::Sections::page_preboil); - double quant = Measurement::amountDisplay(Measurement::Amount{this->bNoteObs->projOg(), Measurement::Units::sp_grav}, - forcedSystemOfMeasurement); + // SmartDigitWidget::setLowLim and SmartDigitWidget::setHighLim take their parameter in canonical units -- in this + // case, SG. + double const quant = this->bNoteObs->projOg(); this->lcdnumber_projectedOG->setLowLim( lowLimitPct * quant); this->lcdnumber_projectedOG->setHighLim(highLimitPct * quant); - Measurement::UnitSystem const & displayUnitSystem = - Measurement::getUnitSystemForField(*PropertyNames::BrewNote::projOg, - *PersistentSettings::Sections::page_preboil, - Measurement::PhysicalQuantity::Density); + Measurement::UnitSystem const & displayUnitSystem = this->label_projectedOg->getDisplayUnitSystem(); int precision = (displayUnitSystem == Measurement::UnitSystems::density_Plato) ? 0 : 3; - this->lcdnumber_projectedOG->display(quant, precision); + // Set precision before setting amount as setPrecision does not update the display, whereas setAmount does + this->lcdnumber_projectedOG->setPrecision(precision); + this->lcdnumber_projectedOG->setAmount(quant); return; } void BrewNoteWidget::setBrewNote(BrewNote* bNote) { + qDebug() << Q_FUNC_INFO << "BrewNote:" << bNote; if (this->bNoteObs) { disconnect(this->bNoteObs, nullptr, this, nullptr); @@ -110,28 +124,28 @@ void BrewNoteWidget::setBrewNote(BrewNote* bNote) { connect(this->bNoteObs, &NamedEntity::changed, this, &BrewNoteWidget::changed); // Set the highs and the lows for the lcds - lcdnumber_effBK->setLowLim (bNoteObs->projEff_pct() * lowLimitPct); - lcdnumber_effBK->setHighLim(bNoteObs->projEff_pct() * highLimitPct); + this->lcdnumber_effBK->setLowLim (bNoteObs->projEff_pct() * lowLimitPct); + this->lcdnumber_effBK->setHighLim(bNoteObs->projEff_pct() * highLimitPct); - lcdnumber_projectedOG->setLowLim (bNoteObs->projOg() * lowLimitPct); - lcdnumber_projectedOG->setHighLim(bNoteObs->projOg() * highLimitPct); + this->lcdnumber_projectedOG->setLowLim (bNoteObs->projOg() * lowLimitPct); + this->lcdnumber_projectedOG->setHighLim(bNoteObs->projOg() * highLimitPct); - lcdnumber_brewhouseEff->setLowLim (bNoteObs->projEff_pct() * lowLimitPct); - lcdnumber_brewhouseEff->setHighLim(bNoteObs->projEff_pct() * highLimitPct); + this->lcdnumber_brewhouseEff->setLowLim (bNoteObs->projEff_pct() * lowLimitPct); + this->lcdnumber_brewhouseEff->setHighLim(bNoteObs->projEff_pct() * highLimitPct); - lcdnumber_projABV->setLowLim (bNoteObs->projABV_pct() * lowLimitPct); - lcdnumber_projABV->setHighLim(bNoteObs->projABV_pct() * highLimitPct); + this->lcdnumber_projABV->setLowLim (bNoteObs->projABV_pct() * lowLimitPct); + this->lcdnumber_projABV->setHighLim(bNoteObs->projABV_pct() * highLimitPct); - lcdnumber_abv->setLowLim (bNoteObs->projABV_pct() * lowLimitPct); - lcdnumber_abv->setHighLim(bNoteObs->projABV_pct() * highLimitPct); + this->lcdnumber_abv->setLowLim (bNoteObs->projABV_pct() * lowLimitPct); + this->lcdnumber_abv->setHighLim(bNoteObs->projABV_pct() * highLimitPct); - lcdnumber_atten->setLowLim (bNoteObs->projAtten() * lowLimitPct); - lcdnumber_atten->setHighLim(bNoteObs->projAtten() * highLimitPct); + this->lcdnumber_atten->setLowLim (bNoteObs->projAtten() * lowLimitPct); + this->lcdnumber_atten->setHighLim(bNoteObs->projAtten() * highLimitPct); - lcdnumber_projAtten->setLowLim (bNoteObs->projAtten() * lowLimitPct); - lcdnumber_projAtten->setHighLim(bNoteObs->projAtten() * highLimitPct); + this->lcdnumber_projAtten->setLowLim (bNoteObs->projAtten() * lowLimitPct); + this->lcdnumber_projAtten->setHighLim(bNoteObs->projAtten() * highLimitPct); - showChanges(); + this->showChanges(); } return; } @@ -140,118 +154,18 @@ bool BrewNoteWidget::isBrewNote(BrewNote* note) { return this->bNoteObs == note; } -void BrewNoteWidget::updateSG() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setSg(lineEdit_SG->toCanonical().quantity()); - return; -} - -void BrewNoteWidget::updateVolumeIntoBK_l() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setVolumeIntoBK_l(lineEdit_volIntoBK->toCanonical().quantity()); - return; -} - -void BrewNoteWidget::updateStrikeTemp_c() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setStrikeTemp_c(lineEdit_strikeTemp->toCanonical().quantity()); - return; -} - -void BrewNoteWidget::updateMashFinTemp_c() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setMashFinTemp_c(lineEdit_mashFinTemp->toCanonical().quantity()); - return; -} - -void BrewNoteWidget::updateOG() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setOg(lineEdit_OG->toCanonical().quantity()); - return; -} - -void BrewNoteWidget::updatePostBoilVolume_l() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setPostBoilVolume_l(lineEdit_postBoilVol->toCanonical().quantity()); - this->showChanges(); - return; -} - -void BrewNoteWidget::updateVolumeIntoFerm_l() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setVolumeIntoFerm_l(lineEdit_volIntoFerm->toCanonical().quantity()); - this->showChanges(); - return; -} - -void BrewNoteWidget::updatePitchTemp_c() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setPitchTemp_c(lineEdit_pitchTemp->toCanonical().quantity()); - this->showChanges(); - return; -} - -void BrewNoteWidget::updateFG() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setFg(lineEdit_FG->toCanonical().quantity()); - this->showChanges(); - return; -} - -void BrewNoteWidget::updateFinalVolume_l() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setFinalVolume_l(lineEdit_finalVol->toCanonical().quantity()); -// this->showChanges(); - return; -} - -void BrewNoteWidget::updateFermentDate(QDate const & datetime) { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setFermentDate(datetime); - return; -} - -void BrewNoteWidget::updateNotes() { - if (this->bNoteObs == nullptr) { - return; - } - - this->bNoteObs->setNotes(btTextEdit_brewNotes->toPlainText() ); - return; -} +void BrewNoteWidget::updateSG() { if (this->bNoteObs) { this->bNoteObs->setSg (this->lineEdit_Sg ->toCanonical().quantity()); } return; } +void BrewNoteWidget::updateVolumeIntoBK_l() { if (this->bNoteObs) { this->bNoteObs->setVolumeIntoBK_l (this->lineEdit_volIntoBk ->toCanonical().quantity()); } return; } +void BrewNoteWidget::updateStrikeTemp_c() { if (this->bNoteObs) { this->bNoteObs->setStrikeTemp_c (this->lineEdit_strikeTemp ->toCanonical().quantity()); } return; } +void BrewNoteWidget::updateMashFinTemp_c() { if (this->bNoteObs) { this->bNoteObs->setMashFinTemp_c (this->lineEdit_mashFinTemp->toCanonical().quantity()); } return; } +void BrewNoteWidget::updateOG() { if (this->bNoteObs) { this->bNoteObs->setOg (this->lineEdit_Og ->toCanonical().quantity()); } return; } +void BrewNoteWidget::updatePostBoilVolume_l() { if (this->bNoteObs) { this->bNoteObs->setPostBoilVolume_l(this->lineEdit_postBoilVol->toCanonical().quantity()); this->showChanges(); } return; } +void BrewNoteWidget::updateVolumeIntoFerm_l() { if (this->bNoteObs) { this->bNoteObs->setVolumeIntoFerm_l(this->lineEdit_volIntoFerm->toCanonical().quantity()); this->showChanges(); } return; } +void BrewNoteWidget::updatePitchTemp_c() { if (this->bNoteObs) { this->bNoteObs->setPitchTemp_c (this->lineEdit_pitchTemp ->toCanonical().quantity()); this->showChanges(); } return; } +void BrewNoteWidget::updateFG() { if (this->bNoteObs) { this->bNoteObs->setFg (this->lineEdit_Fg ->toCanonical().quantity()); this->showChanges(); } return; } +void BrewNoteWidget::updateFinalVolume_l() { if (this->bNoteObs) { this->bNoteObs->setFinalVolume_l (this->lineEdit_finalVolume->toCanonical().quantity()); } return; } +void BrewNoteWidget::updateFermentDate(QDate const & datetime) { if (this->bNoteObs) { this->bNoteObs->setFermentDate (datetime); } return; } +void BrewNoteWidget::updateNotes() { if (this->bNoteObs) { this->bNoteObs->setNotes (this->btTextEdit_brewNotes->toPlainText() ); } return; } void BrewNoteWidget::changed([[maybe_unused]] QMetaProperty prop, [[maybe_unused]] QVariant val) { @@ -264,35 +178,34 @@ void BrewNoteWidget::changed([[maybe_unused]] QMetaProperty prop, } void BrewNoteWidget::showChanges([[maybe_unused]] QString field) { - if (this->bNoteObs == nullptr) { + if (!this->bNoteObs) { return; } - lineEdit_SG->setText(bNoteObs); - lineEdit_volIntoBK->setText(bNoteObs); - lineEdit_strikeTemp->setText(bNoteObs); - lineEdit_mashFinTemp->setText(bNoteObs); - lineEdit_OG->setText(bNoteObs); - lineEdit_postBoilVol->setText(bNoteObs); - lineEdit_volIntoFerm->setText(bNoteObs); - lineEdit_pitchTemp->setText(bNoteObs); - lineEdit_FG->setText(bNoteObs); - lineEdit_finalVol->setText(bNoteObs); - - lineEdit_fermentDate->setDate(bNoteObs->fermentDate()); - btTextEdit_brewNotes->setPlainText(bNoteObs->notes()); + this->lineEdit_Sg ->setAmount (bNoteObs->sg ()); + this->lineEdit_volIntoBk ->setAmount (bNoteObs->volumeIntoBK_l ()); + this->lineEdit_strikeTemp ->setAmount (bNoteObs->strikeTemp_c ()); + this->lineEdit_mashFinTemp->setAmount (bNoteObs->mashFinTemp_c ()); + this->lineEdit_Og ->setAmount (bNoteObs->og ()); + this->lineEdit_postBoilVol->setAmount (bNoteObs->postBoilVolume_l()); + this->lineEdit_volIntoFerm->setAmount (bNoteObs->volumeIntoFerm_l()); + this->lineEdit_pitchTemp ->setAmount (bNoteObs->pitchTemp_c ()); + this->lineEdit_Fg ->setAmount (bNoteObs->fg ()); + this->lineEdit_finalVolume->setAmount (bNoteObs->finalVolume_l ()); + this->lineEdit_fermentDate->setDate (bNoteObs->fermentDate ()); + this->btTextEdit_brewNotes->setPlainText(bNoteObs->notes ()); // Now with the calculated stuff - lcdnumber_effBK->display(bNoteObs->effIntoBK_pct(),2); + this->lcdnumber_effBK->setAmount(bNoteObs->effIntoBK_pct()); // Need to think about these? Maybe use the bubbles? this->updateProjOg(); // this requires more work, but updateProj does it - lcdnumber_brewhouseEff->display(bNoteObs->brewhouseEff_pct(),2); - lcdnumber_projABV->display(bNoteObs->projABV_pct(),2); - lcdnumber_abv->display(bNoteObs->abv(),2); - lcdnumber_atten->display(bNoteObs->attenuation(),2); - lcdnumber_projAtten->display(bNoteObs->projAtten(),2); + this->lcdnumber_brewhouseEff->setAmount(bNoteObs->brewhouseEff_pct()); + this->lcdnumber_projABV ->setAmount(bNoteObs->projABV_pct ()); + this->lcdnumber_abv ->setAmount(bNoteObs->abv ()); + this->lcdnumber_atten ->setAmount(bNoteObs->attenuation ()); + this->lcdnumber_projAtten ->setAmount(bNoteObs->projAtten ()); return; } diff --git a/src/BtAmountEdit.cpp b/src/BtAmountEdit.cpp deleted file mode 100644 index 8ec78124..00000000 --- a/src/BtAmountEdit.cpp +++ /dev/null @@ -1,216 +0,0 @@ -/*====================================================================================================================== - * BtAmountEdit.cpp is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Mark de Wever - * • Mattias Måhl - * • Matt Young - * • Mike Evans - * • Mik Firestone - * • Philip Greggory Lee - * • Théophane Martin - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "BtAmountEdit.h" - -#include - -#include "Localization.h" -#include "measurement/Measurement.h" -#include "model/NamedEntity.h" -#include "utils/OptionalHelpers.h" - -BtAmountEdit::BtAmountEdit(QWidget *parent, - Measurement::PhysicalQuantities const physicalQuantities, - Measurement::Unit const * units, - int const defaultPrecision, - QString const & maximalDisplayString) : - BtLineEdit{parent, - ConvertToBtFieldType(physicalQuantities), - defaultPrecision, - maximalDisplayString}, - UiAmountWithUnits{parent, physicalQuantities, units} { - this->configSection = property("configSection").toString(); - - connect(this, &QLineEdit::editingFinished, this, &BtAmountEdit::onLineChanged); - - return; -} - -BtAmountEdit::~BtAmountEdit() = default; - -QString BtAmountEdit::getWidgetText() const { - return this->text(); -} - -void BtAmountEdit::setWidgetText(QString text) { - this->QLineEdit::setText(text); - return; -} - -void BtAmountEdit::setText(double amount) { - this->setText(amount, this->defaultPrecision); - return; -} - -void BtAmountEdit::setText(double amount, int precision) { - this->QLineEdit::setText(this->displayAmount(amount, precision)); - this->setDisplaySize(); - return; -} - -void BtAmountEdit::setText(NamedEntity * element) { - this->setText(element, this->defaultPrecision); - return; -} - -void BtAmountEdit::setText(NamedEntity * element, int precision) { - char const * const propertyName = this->editField.toLatin1().constData(); - QVariant const propertyValue = element->property(propertyName); - qDebug() << - Q_FUNC_INFO << "Read property" << this->editField << "(" << propertyName << ") of" << *element << "as" << - propertyValue; - - bool force = false; - QString display; - if (propertyValue.canConvert(QVariant::Double)) { - bool ok = false; - // It is important here to use QVariant::toDouble() instead of going - // through toString() and then Localization::toDouble(). - double amount = propertyValue.toDouble(&ok); - if (!ok) { - qWarning() << - Q_FUNC_INFO << "Could not convert " << propertyValue.toString() << " (" << this->configSection << ":" << - this->editField << ") to double"; - } - - display = this->displayAmount(amount, precision); - } else { - display = "?"; - } - - this->QLineEdit::setText(display); - this->setDisplaySize(force); - return; -} - -void BtAmountEdit::setText(QString amount) { - this->setText(amount, this->defaultPrecision); - return; -} - -void BtAmountEdit::setText(QString amount, int precision) { - bool ok = false; - double amt = Localization::toDouble(amount, &ok); - if (!ok) { - qWarning() << - Q_FUNC_INFO << "Could not convert" << amount << "(" << this->configSection << ":" << this->editField << - ") to double"; - } - this->QLineEdit::setText(this->displayAmount(amt, precision)); - - this->setDisplaySize(false); - return; -} - -void BtAmountEdit::setText(QVariant amount) { - this->setText(amount, this->defaultPrecision); - return; -} - -void BtAmountEdit::setText(QVariant amount, int precision) { - this->setText(amount.toString(), precision); - return; -} - -void BtAmountEdit::onLineChanged() { - auto const myFieldType = this->getFieldType(); - qDebug() << - Q_FUNC_INFO << "this->fieldType=" << myFieldType << ", this->units=" << this->units << - ", forcedSystemOfMeasurement=" << this->getForcedSystemOfMeasurement() << ", forcedRelativeScale=" << - this->getForcedRelativeScale() << ", value=" << this->text(); - - Measurement::PhysicalQuantities const physicalQuantities = ConvertToPhysicalQuantities(myFieldType); - - Measurement::SystemOfMeasurement const oldSystemOfMeasurement = - Measurement::getSystemOfMeasurementForField(this->editField, - this->configSection, - physicalQuantities); - auto oldForcedRelativeScale = Measurement::getForcedRelativeScaleForField(this->editField, this->configSection); - PreviousScaleInfo previousScaleInfo{ - oldSystemOfMeasurement, - oldForcedRelativeScale - }; - - qDebug() << - Q_FUNC_INFO << "oldSystemOfMeasurement=" << oldSystemOfMeasurement << ", oldForcedRelativeScale=" << - oldForcedRelativeScale; - - this->lineChanged(previousScaleInfo); - return; -} - -void BtAmountEdit::lineChanged(PreviousScaleInfo previousScaleInfo) { - // editingFinished happens on focus being lost, regardless of anything - // being changed. I am hoping this short circuits properly and we do - // nothing if nothing changed. - if (this->sender() == this && !isModified()) { - qDebug() << Q_FUNC_INFO << "Nothing changed; field holds" << this->text(); - return; - } - - this->textOrUnitsChanged(previousScaleInfo); - - if (sender() == this) { - emit textModified(); - } - - return; -} - -// TBD Can we not work out the canonical units here automatically? -BtMassEdit ::BtMassEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Mass , &Measurement::Units::kilograms ) { return; } -BtVolumeEdit ::BtVolumeEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Volume , &Measurement::Units::liters ) { return; } -BtTimeEdit ::BtTimeEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Time , &Measurement::Units::minutes , 3) { return; } -BtTemperatureEdit ::BtTemperatureEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Temperature , &Measurement::Units::celsius , 1) { return; } -BtColorEdit ::BtColorEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Color , &Measurement::Units::srm ) { return; } -BtDensityEdit ::BtDensityEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Density , &Measurement::Units::sp_grav ) { return; } -BtDiastaticPowerEdit ::BtDiastaticPowerEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::DiastaticPower , &Measurement::Units::lintner ) { return; } -BtAcidityEdit ::BtAcidityEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Acidity , &Measurement::Units::pH ) { return; } -BtBitternessEdit ::BtBitternessEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Bitterness , &Measurement::Units::ibu ) { return; } -BtCarbonationEdit ::BtCarbonationEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Carbonation , &Measurement::Units::carbonationVolumes ) { return; } -BtMassConcentrationEdit ::BtMassConcentrationEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::MassConcentration , &Measurement::Units::partsPerMillion ) { return; } -BtVolumeConcentrationEdit ::BtVolumeConcentrationEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::VolumeConcentration , &Measurement::Units::milligramsPerLiter ) { return; } -BtViscosityEdit ::BtViscosityEdit (QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::Viscosity , &Measurement::Units::centipoise ) { return; } -BtSpecificHeatCapacityEdit::BtSpecificHeatCapacityEdit(QWidget *parent) : BtAmountEdit(parent, Measurement::PhysicalQuantity::SpecificHeatCapacity, &Measurement::Units::caloriesPerCelsiusPerGram) { return; } - - -BtMixedMassOrVolumeEdit::BtMixedMassOrVolumeEdit(QWidget *parent) : - BtAmountEdit(parent, - Measurement::PqEitherMassOrVolume, - // This is probably pure evil I will later regret - &Measurement::Units::liters) { - return; -} - -void BtMixedMassOrVolumeEdit::setIsWeight(bool state) { - // But you have to admit, this is clever - if (state) { - this->units = &Measurement::Units::kilograms; - } else { - this->units = &Measurement::Units::liters; - } - - // maybe? My head hurts now - this->onLineChanged(); - return; -} diff --git a/src/BtAmountEdit.h b/src/BtAmountEdit.h deleted file mode 100644 index 1f65b79e..00000000 --- a/src/BtAmountEdit.h +++ /dev/null @@ -1,115 +0,0 @@ -/*====================================================================================================================== - * BtAmountEdit.h is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Mark de Wever - * • Matt Young - * • Mike Evans - * • Mik Firestone - * • Philip Greggory Lee - * • Scott Peshak - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#ifndef BTAMOUNTEDIT_H -#define BTAMOUNTEDIT_H -#pragma once - -#include "BtLineEdit.h" -#include "measurement/PhysicalQuantity.h" -#include "measurement/Unit.h" -#include "measurement/UnitSystem.h" -#include "UiAmountWithUnits.h" - -/** - * \brief Extends \c BtLineEdit for any numerical field where the user has a choice of units. Handles all the unit - * transformations. - * - * NB: Per https://doc.qt.io/qt-5/moc.html#multiple-inheritance-requires-qobject-to-be-first, "If you are using - * multiple inheritance, moc [Qt's Meta-Object Compiler] assumes that the first inherited class is a subclass of - * QObject. Also, be sure that only the first inherited class is a QObject." In particular, this means we must - * put Q_PROPERTY declarations for UiAmountWithUnits attributes here rather than in UiAmountWithUnits itself. - */ -class BtAmountEdit : public BtLineEdit, public UiAmountWithUnits { - Q_OBJECT - - Q_PROPERTY(QString configSection READ getConfigSection WRITE setConfigSection STORED false) - Q_PROPERTY(QString editField READ getEditField WRITE setEditField STORED false) - Q_PROPERTY(QString forcedSystemOfMeasurement READ getForcedSystemOfMeasurementViaString WRITE setForcedSystemOfMeasurementViaString STORED false) - Q_PROPERTY(QString forcedRelativeScale READ getForcedRelativeScaleViaString WRITE setForcedRelativeScaleViaString STORED false) - -public: - BtAmountEdit(QWidget * parent, - Measurement::PhysicalQuantities const physicalQuantities, - Measurement::Unit const * units, - int const defaultPrecision = 3, - QString const & maximalDisplayString = "100.000 srm"); - virtual ~BtAmountEdit(); - - /** - * \see \c UiAmountWithUnits for what this member function needs to do - */ - virtual QString getWidgetText() const; - - /** - * \see \c UiAmountWithUnits for what this member function needs to do - */ - virtual void setWidgetText(QString text); - - // Use one of these when you just want to set the text - void setText(NamedEntity* element); - void setText(NamedEntity* element, int precision); - void setText(double amount); - void setText(double amount, int precision); - void setText(QString amount); - void setText(QString amount, int precision); - void setText(QVariant amount); - void setText(QVariant amount, int precision); - -public slots: - void onLineChanged(); - - /** - * \brief Received from \c BtLabel when the user has change \c UnitSystem - * - * This is mostly referenced in .ui files. (NB this means that the signal connections are only checked at run-time.) - */ - void lineChanged(PreviousScaleInfo previousScaleInfo); -}; - -// -// See comment in BtLineEdit.h for why we need all these trivial child classes to use in .ui files -// -class BtMassEdit : public BtAmountEdit { Q_OBJECT public: BtMassEdit (QWidget* parent); }; -class BtVolumeEdit : public BtAmountEdit { Q_OBJECT public: BtVolumeEdit (QWidget* parent); }; -class BtTimeEdit : public BtAmountEdit { Q_OBJECT public: BtTimeEdit (QWidget* parent); }; -class BtTemperatureEdit : public BtAmountEdit { Q_OBJECT public: BtTemperatureEdit (QWidget* parent); }; -class BtColorEdit : public BtAmountEdit { Q_OBJECT public: BtColorEdit (QWidget* parent); }; -class BtDensityEdit : public BtAmountEdit { Q_OBJECT public: BtDensityEdit (QWidget* parent); }; -class BtDiastaticPowerEdit : public BtAmountEdit { Q_OBJECT public: BtDiastaticPowerEdit (QWidget* parent); }; -class BtAcidityEdit : public BtAmountEdit { Q_OBJECT public: BtAcidityEdit (QWidget* parent); }; -class BtBitternessEdit : public BtAmountEdit { Q_OBJECT public: BtBitternessEdit (QWidget* parent); }; -class BtCarbonationEdit : public BtAmountEdit { Q_OBJECT public: BtCarbonationEdit (QWidget* parent); }; -class BtMassConcentrationEdit : public BtAmountEdit { Q_OBJECT public: BtMassConcentrationEdit (QWidget* parent); }; -class BtVolumeConcentrationEdit : public BtAmountEdit { Q_OBJECT public: BtVolumeConcentrationEdit (QWidget* parent); }; -class BtViscosityEdit : public BtAmountEdit { Q_OBJECT public: BtViscosityEdit (QWidget* parent); }; -class BtSpecificHeatCapacityEdit : public BtAmountEdit { Q_OBJECT public: BtSpecificHeatCapacityEdit(QWidget* parent); }; - -// So-called "mixed" objects, ie ones where we accept two different types of measurement (eg Mass and Volume) are a pain -class BtMixedMassOrVolumeEdit : public BtAmountEdit { - Q_OBJECT -public: - BtMixedMassOrVolumeEdit(QWidget* parent); -public slots: - void setIsWeight(bool state); -}; - -#endif diff --git a/src/BtFieldType.cpp b/src/BtFieldType.cpp index 4bd9fe36..acb0b039 100644 --- a/src/BtFieldType.cpp +++ b/src/BtFieldType.cpp @@ -27,6 +27,7 @@ QString GetDisplayName(NonPhysicalQuantity nonPhysicalQuantity) { case NonPhysicalQuantity::Count : return "Count" ; case NonPhysicalQuantity::Percentage : return "Percentage" ; case NonPhysicalQuantity::Bool : return "Bool" ; + case NonPhysicalQuantity::Enum : return "Enum" ; case NonPhysicalQuantity::Dimensionless: return "Dimensionless"; // In C++23, we'd add: // default: std::unreachable(); diff --git a/src/BtFieldType.h b/src/BtFieldType.h index 2ab6b4b4..ba77ed88 100644 --- a/src/BtFieldType.h +++ b/src/BtFieldType.h @@ -34,10 +34,14 @@ enum class NonPhysicalQuantity { Count, Percentage, Bool, + /** + * This usually means we need a \c BtComboBox + */ + Enum, /** * \brief This is for a number that has no units, not even pseudo ones. It is currently a bit over-used -- ie there - * are places we are using this (typically via BtNumberOnlyEdit) where we probably should be using a - * \c PhysicalQuantity. We should fix these over time. + * are places we are using this where we probably should be using a \c PhysicalQuantity. We should fix these + * over time. */ Dimensionless, }; diff --git a/src/BtLabel.cpp b/src/BtLabel.cpp deleted file mode 100644 index 4bd5e37e..00000000 --- a/src/BtLabel.cpp +++ /dev/null @@ -1,281 +0,0 @@ -/*====================================================================================================================== - * BtLabel.cpp is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Mark de Wever - * • Matt Young - * • Mik Firestone - * • Philip Greggory Lee - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "BtLabel.h" - -#include -#include -#include - -#include "BtAmountEdit.h" -#include "measurement/Measurement.h" -#include "model/Style.h" -#include "model/Recipe.h" -#include "PersistentSettings.h" -#include "utils/OptionalHelpers.h" -#include "widgets/UnitAndScalePopUpMenu.h" - -BtLabel::BtLabel(QWidget *parent, - BtFieldType fieldType) : - QLabel{parent}, - fieldType{fieldType}, - btParent{parent}, - contextMenu{nullptr} { - connect(this, &QWidget::customContextMenuRequested, this, &BtLabel::popContextMenu); - return; -} - -BtLabel::~BtLabel() = default; - -BtLineEdit & BtLabel::getBuddy() const { - // Call QLabel's built-in function to get the buddy - QWidget * buddy = this->buddy(); - - // We assert that it's a coding error for there not to be a buddy! - Q_ASSERT(buddy); - - return static_cast(*buddy); -} - -void BtLabel::enterEvent([[maybe_unused]] QEvent * event) { - this->textEffect(true); - return; -} - -void BtLabel::leaveEvent([[maybe_unused]] QEvent * event) { - this->textEffect(false); - return; -} - -void BtLabel::mouseReleaseEvent(QMouseEvent * event) { - // For the moment, we want left-click and right-click to have the same effect, so when we get a left-click event, we - // send ourselves the right-click signal, which will then fire BtLabel::popContextMenu(). - emit this->QWidget::customContextMenuRequested(event->pos()); - return; -} - -void BtLabel::textEffect(bool enabled) { - QFont myFont = this->font(); - myFont.setUnderline(enabled); - this->setFont(myFont); - return; -} - -void BtLabel::initializeSection() { - if (!this->configSection.isEmpty()) { - return; - } - - // as much as I dislike it, dynamic properties can't be referenced on - // initialization. - QWidget * mybuddy = this->buddy(); - - // - // If the label has the configSection defined, use it - // otherwise, if the paired field has a configSection, use it - // otherwise, if the parent object has a configSection, use it - // if all else fails, get the parent's object name - // - if (this->property("configSection").isValid()) { - this->configSection = property("configSection").toString(); - } else if (mybuddy && mybuddy->property("configSection").isValid() ) { - this->configSection = mybuddy->property("configSection").toString(); - } else if (this->btParent->property("configSection").isValid() ) { - this->configSection = this->btParent->property("configSection").toString(); - } else { - qWarning() << Q_FUNC_INFO << "this failed" << this; - this->configSection = this->btParent->objectName(); - } - return; -} - -void BtLabel::initializeProperty() { - - if (!this->propertyName.isEmpty()) { - return; - } - - QWidget* mybuddy = this->buddy(); - if (this->property("editField").isValid()) { - this->propertyName = this->property("editField").toString(); - } else if (mybuddy && mybuddy->property("editField").isValid()) { - this->propertyName = mybuddy->property("editField").toString(); - } else { - qWarning() << Q_FUNC_INFO << "That failed miserably"; - } - return; -} - - - -void BtLabel::initializeMenu() { - // If a context menu already exists, we need to delete it and recreate it. We can't always reuse an existing menu - // because the sub-menu for relative scale needs to change when a different unit system is selected. (In theory we - // could only recreate the context menu when a different unit system is selected, but that adds complication.) - if (this->contextMenu) { - // NB: Although the existing menu is "owned" by this->btParent, it is fine for us to delete it here. The Qt - // ownership in this context merely guarantees that this->btParent will, in its own destructor, delete the menu if - // it still exists. - delete this->contextMenu; - this->contextMenu = nullptr; - } - - std::optional forcedSystemOfMeasurement = - Measurement::getForcedSystemOfMeasurementForField(this->propertyName, this->configSection); - std::optional forcedRelativeScale = - Measurement::getForcedRelativeScaleForField(this->propertyName, this->configSection); - qDebug() << - Q_FUNC_INFO << "forcedSystemOfMeasurement=" << forcedSystemOfMeasurement << ", forcedRelativeScale=" << - forcedRelativeScale; - - auto fieldType = this->getBuddy().getFieldType(); - if (std::holds_alternative(fieldType)) { - return; - } - - // Since fieldType is not this->getBuddy(), we know our buddy is BtAmountEdit, not just BtLineEdit, so this cast - // should be safe. - BtAmountEdit & amountBuddy = static_cast(this->getBuddy()); - Measurement::PhysicalQuantity physicalQuantity = amountBuddy.getPhysicalQuantity(); - this->contextMenu = UnitAndScalePopUpMenu::create(this->btParent, - physicalQuantity, - forcedSystemOfMeasurement, - forcedRelativeScale); - return; -} - -void BtLabel::popContextMenu(const QPoint& point) { - // For the moment, at least, we do not allow people to choose date formats per-field. (Although you might want to - // mix and match metric and imperial systems in certain circumstances, it's less clear that there's a benefit to - // mixing and matching date formats.) - if (!std::holds_alternative(this->fieldType)) { - return; - } - - QObject* calledBy = sender(); - if (calledBy == nullptr) { - return; - } - - QWidget * widgie = qobject_cast(calledBy); - if (widgie == nullptr) { - return; - } - - this->initializeProperty(); - this->initializeSection(); - this->initializeMenu(); - - // Show the pop-up menu and get back whatever the user seleted - QAction * invoked = this->contextMenu->exec(widgie->mapToGlobal(point)); - if (invoked == nullptr) { - return; - } - - // Save the current settings (which may come from system-wide defaults) for the signal below - Q_ASSERT(std::holds_alternative(this->fieldType)); - Measurement::PhysicalQuantity physicalQuantity = std::get(this->fieldType); - PreviousScaleInfo previousScaleInfo{ - Measurement::getSystemOfMeasurementForField(this->propertyName, this->configSection, physicalQuantity), - Measurement::getForcedRelativeScaleForField(this->propertyName, this->configSection) - }; - - // To make this all work, we need to set ogMin and ogMax when og is set etc - QVector fieldsToSet; - fieldsToSet.append(this->propertyName); - if (this->propertyName == "og") { - fieldsToSet.append(QString(*PropertyNames::Style::ogMin)); - fieldsToSet.append(QString(*PropertyNames::Style::ogMax)); - } else if (this->propertyName == "fg") { - fieldsToSet.append(QString(*PropertyNames::Style::fgMin)); - fieldsToSet.append(QString(*PropertyNames::Style::fgMax)); - } else if (this->propertyName == "color_srm") { - fieldsToSet.append(QString(*PropertyNames::Style::colorMin_srm)); - fieldsToSet.append(QString(*PropertyNames::Style::colorMax_srm)); - } - - // User will either have selected a SystemOfMeasurement or a UnitSystem::RelativeScale. We can know which based on - // whether it's the menu or the sub-menu that it came from. - bool isTopMenu{invoked->parentWidget() == this->contextMenu}; - if (isTopMenu) { - // It's the menu, so SystemOfMeasurement - std::optional whatSelected = - UnitAndScalePopUpMenu::dataFromQAction(*invoked); - qDebug() << Q_FUNC_INFO << "Selected SystemOfMeasurement" << whatSelected; - if (!whatSelected) { - // Null means "Default", which means don't set a forced SystemOfMeasurement for this field - for (auto field : fieldsToSet) { - Measurement::setForcedSystemOfMeasurementForField(field, this->configSection, std::nullopt); - } - } else { - for (auto field : fieldsToSet) { - Measurement::setForcedSystemOfMeasurementForField(field, this->configSection, *whatSelected); - } - } - // Choosing a forced SystemOfMeasurement resets any selection of forced RelativeScale - for (auto field : fieldsToSet) { - Measurement::setForcedRelativeScaleForField(field, this->configSection, std::nullopt); - } - - // - // Hmm. For the color fields, we want to include the ecb or srm in the label text here. - // - // Assert that we already bailed above for fields that aren't a PhysicalQuantity, so we know std::get won't throw - // here. - // - Q_ASSERT(std::holds_alternative(this->fieldType)); - if (Measurement::PhysicalQuantity::Color == std::get(this->fieldType)) { - Measurement::UnitSystem const & disp = - Measurement::getUnitSystemForField(this->propertyName, - this->configSection, - Measurement::PhysicalQuantity::Color); - this->setText(tr("Color (%1)").arg(disp.unit()->name)); - } - } else { - // It's the sub-menu, so UnitSystem::RelativeScale - std::optional whatSelected = - UnitAndScalePopUpMenu::dataFromQAction(*invoked); - qDebug() << Q_FUNC_INFO << "Selected RelativeScale" << whatSelected; - if (!whatSelected) { - // Null means "Default", which means don't set a forced RelativeScale for this field - for (auto field : fieldsToSet) { - Measurement::setForcedRelativeScaleForField(field, this->configSection, std::nullopt); - } - } else { - for (auto field : fieldsToSet) { - Measurement::setForcedRelativeScaleForField(field, this->configSection, *whatSelected); - } - } - } - - // Remember, we need the original unit, not the new one. - emit changedSystemOfMeasurementOrScale(previousScaleInfo); - - return; -} - -BtColorLabel ::BtColorLabel (QWidget *parent) : BtLabel(parent, Measurement::PhysicalQuantity::Color ) { return; } -BtDateLabel ::BtDateLabel (QWidget *parent) : BtLabel(parent, NonPhysicalQuantity::Date ) { return; } -BtDensityLabel ::BtDensityLabel (QWidget *parent) : BtLabel(parent, Measurement::PhysicalQuantity::Density ) { return; } -BtMassLabel ::BtMassLabel (QWidget *parent) : BtLabel(parent, Measurement::PhysicalQuantity::Mass ) { return; } -BtMixedMassOrVolumeLabel::BtMixedMassOrVolumeLabel(QWidget *parent) : BtLabel(parent, Measurement::PqEitherMassOrVolume ) { return; } -BtTemperatureLabel ::BtTemperatureLabel (QWidget *parent) : BtLabel(parent, Measurement::PhysicalQuantity::Temperature ) { return; } -BtTimeLabel ::BtTimeLabel (QWidget *parent) : BtLabel(parent, Measurement::PhysicalQuantity::Time ) { return; } -BtVolumeLabel ::BtVolumeLabel (QWidget *parent) : BtLabel(parent, Measurement::PhysicalQuantity::Volume ) { return; } -BtDiastaticPowerLabel ::BtDiastaticPowerLabel (QWidget *parent) : BtLabel(parent, Measurement::PhysicalQuantity::DiastaticPower) { return; } diff --git a/src/BtLabel.h b/src/BtLabel.h deleted file mode 100644 index 1799f4fb..00000000 --- a/src/BtLabel.h +++ /dev/null @@ -1,167 +0,0 @@ -/*====================================================================================================================== - * BtLabel.h is part of Brewken, and is copyright the following authors 2009-2023: - * • Mark de Wever - * • Matt Young - * • Mik Firestone - * • Philip Greggory Lee - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#ifndef BTLABEL_H -#define BTLABEL_H -#pragma once - -#include - -#include -#include -#include -#include -#include - -#include "BtFieldType.h" -#include "BtLineEdit.h" -#include "measurement/UnitSystem.h" -#include "UiAmountWithUnits.h" // For PreviousScaleInfo - -/*! - * \class BtLabel - * - * \brief Performs the necessary magic to select display units for any label. Specifically, this allows the user to - * right-click on the label for a field and select - * (a) which unit system to use for that field (eg US Customary (mass), Imperial (mass) or Metric/SI (mass) - * for a weight field) - * (b) which units within that system to use for the field (eg kg, g, mg if the user has selected Metric/SI on - * a weight field). - * Moreover, the settings for each label are remembered (via PersistentSettings) for future times the program is - * run. - * - * This has been a rather hidden feature of the program as there were no visual clues that right-clicking on a - * field label would bring up a useful menu (and it is not common behaviour in other software). Where possible, - * we have now made it so that - * • mouseover on the label underlines the label text (hopefully making the user think of a clickable link), - * • where left-clicking would otherwise have no effect, it now has the same effect as right-click. - * - * A \c BtLabel (or subclass thereof) will usually have a corresponding \c BtLineEdit (or subclass thereof). - * These two widgets will be Qt buddies, which just means that the \c BtLineEdit accepts the input focus on - * behalf of the \c BtLabel when the user types the label's shortcut key combination. - * - * When the \c BtLabel needs to tell the \c BtLineEdit that the \c UnitSystem etc has changed, it sends a - * \c changedUnitSystemOrScale signal. (Previously this signal was called \c labelChanged.) - */ -class BtLabel : public QLabel { - Q_OBJECT - -public: - /*! - * \brief Initialize the BtLabel with the parent and do some things with the type - * - * \param parent - QWidget* to the parent object - * \param lType - the type of label: none, gravity, mass or volume - * - * \todo Not sure if I can get the name of the widget being created. - * Not sure how to signal the parent to redisplay - */ - BtLabel(QWidget * parent, BtFieldType fieldType); - virtual ~BtLabel(); - - /** - * \brief Our "buddy" should always be a \c BtLabel. This is a convenience function to get it without the caller - * having to downcast from \c QWidget etc. - */ - BtLineEdit & getBuddy() const; - - /** - * \brief We override the \c QWidget event handlers \c enterEvent and \c leaveEvent to implement mouse-over effects - * on the label text - specifically to give the user a visual clue that the label text is (right)-clickable - */ - virtual void enterEvent(QEvent* event); - virtual void leaveEvent(QEvent* event); - - /** - * \brief We override the \c QWidget event handler \c mouseReleaseEvent to capture left mouse clicks on us. (Right - * clicks get notified to us via the \c QWidget::customContextMenuRequested signal.) - */ - virtual void mouseReleaseEvent(QMouseEvent * event); - - -private: - void textEffect(bool enabled); - -public slots: - /** - * \brief Shows the pop-up menu to allow the user to override the units and/or scale for this field - */ - void popContextMenu(const QPoint &point); - -signals: - /** - * \brief Signal to say we changed the forced system of measurement and/or scale for a field (or group of fields) - * - * NB: This is mostly referenced in .ui files, which compile to string-based connection syntax (see - * https://doc.qt.io/qt-5/signalsandslots-syntaxes.html for difference from functor-based syntax that we - * generally prefer to use in .cpp files). Note too that, if you are manually editing a .ui file rather than - * using Qt Designer, you must NOT put parameter names in the function declarations in the \ and - * \ tags inside the \ tag. - * - * The idea is that fields affected by a change in forced system of measurement or scale (including to/from - * "default") can take current value, convert it to Metric/SI under the "old" settings, then redisplay it with - * whatever the new settings are. Because the fields don't store the "old" settings, we have to send them. - * (They can get the new ones just by calling \c Measurement::getUnitSystemForField() etc. - * - * There will always be an old \c SystemOfMeasurement, even if it's the global default for this field's - * \c PhysicalQuantity. There might not be an old \c RelativeScale though, hence the \c std::optional. - * - * .:TODO:. Fix this comment and/or the code - * Note that we are OK to use std::optional here as, per https://doc.qt.io/qt-5/signalsandslots.html, "Signals - * and slots can take any number of arguments of any type. They are completely type safe." HOWEVER, when - * referring to the function signature in .ui files, we need to remember to escape '<' to "<" and '>' to - * ">" because .ui files are XML. - */ - void changedSystemOfMeasurementOrScale(PreviousScaleInfo previousScaleInfo); - -// Using protected instead of private allows me to not use the friends -// declaration -protected: - BtFieldType fieldType; - QString propertyName; - QString configSection; - QWidget *btParent; - QMenu* contextMenu; - - void initializeSection(); - void initializeProperty(); - void initializeMenu(); - -}; - -// -// These are trivial specialisations of BtLabel that make it possible to use specific types of BtLabel in .ui files. -// It's a bit of a sledgehammer way to pass in a constructor parameter but seems necessary because of limitations in Qt. -// -// AFAIK there is no way to pass constructor parameters to an object in a .ui file. (If you want to do that, the advice -// seems to be to build the layout manually in C++ code.) -// -// Similarly, we might think to template BtLabel, but the Qt Meta-Object Compiler (moc) doesn't understand C++ -// templates, so we can't do that for classes that need to use the Q_OBJECT macro (required for classes that declare -// their own signals and slots or that use other services provided by Qt's meta-object system). -// -class BtColorLabel : public BtLabel { Q_OBJECT public: BtColorLabel (QWidget* parent = nullptr); }; -class BtDensityLabel : public BtLabel { Q_OBJECT public: BtDensityLabel (QWidget* parent = nullptr); }; -class BtMassLabel : public BtLabel { Q_OBJECT public: BtMassLabel (QWidget* parent = nullptr); }; -class BtTemperatureLabel : public BtLabel { Q_OBJECT public: BtTemperatureLabel (QWidget* parent = nullptr); }; -class BtVolumeLabel : public BtLabel { Q_OBJECT public: BtVolumeLabel (QWidget* parent = nullptr); }; -class BtTimeLabel : public BtLabel { Q_OBJECT public: BtTimeLabel (QWidget* parent = nullptr); }; -class BtMixedMassOrVolumeLabel : public BtLabel { Q_OBJECT public: BtMixedMassOrVolumeLabel(QWidget* parent = nullptr); }; -class BtDateLabel : public BtLabel { Q_OBJECT public: BtDateLabel (QWidget* parent = nullptr); }; -class BtDiastaticPowerLabel : public BtLabel { Q_OBJECT public: BtDiastaticPowerLabel (QWidget* parent = nullptr); }; -#endif diff --git a/src/BtLineEdit.cpp b/src/BtLineEdit.cpp deleted file mode 100644 index cd1d0ccc..00000000 --- a/src/BtLineEdit.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/*====================================================================================================================== - * BtLineEdit.cpp is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Mark de Wever - * • Mattias Måhl - * • Matt Young - * • Mike Evans - * • Mik Firestone - * • Philip Greggory Lee - * • Théophane Martin - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "BtLineEdit.h" - -#include -#include -#include - -#include "Algorithms.h" -#include "Localization.h" -#include "Logging.h" -#include "measurement/Measurement.h" -#include "model/NamedEntity.h" -#include "PersistentSettings.h" -#include "utils/OptionalHelpers.h" - -namespace { - int const min_text_size = 8; - int const max_text_size = 50; -} - -BtLineEdit::BtLineEdit(QWidget *parent, - BtFieldType fieldType, - int const defaultPrecision, - QString const & maximalDisplayString) : - QLineEdit{parent}, - fieldType{fieldType}, - defaultPrecision{defaultPrecision} { - - if (std::holds_alternative(fieldType)) { - connect(this, &QLineEdit::editingFinished, this, &BtLineEdit::onLineChanged); - } - - // We can work out (and store) our display size here, but not yet set it. The way the Designer UI Files work is to - // generate code that calls setters such as setMaximumWidth() etc, which would override anything we do here in the - // constructor. So we set our size when setText() is called. - this->calculateDisplaySize(maximalDisplayString); - - return; -} - -BtLineEdit::~BtLineEdit() = default; - -BtFieldType const BtLineEdit::getFieldType() const { - return this->fieldType; -} - -template T BtLineEdit::getValueAs() const { - qDebug() << Q_FUNC_INFO << "Converting" << this->text() << "to" << Measurement::extractRawFromString(this->text()); - return Measurement::extractRawFromString(this->text()); -} -// -// Instantiate the above template function for the types that are going to use it -// (This is all just a trick to allow the template definition to be here in the .cpp file and not in the header, which -// saves having to put a bunch of std::string stuff there.) -// -template int BtLineEdit::getValueAs() const; -template unsigned int BtLineEdit::getValueAs() const; -template double BtLineEdit::getValueAs() const; - -void BtLineEdit::onLineChanged() { - qDebug() << Q_FUNC_INFO; - if (sender() == this) { - emit textModified(); - } - return; -} - -void BtLineEdit::setText(std::optional amount, std::optional precision) { - // For percentages, we'd like to show the % symbol after the number - QString symbol{""}; - if (NonPhysicalQuantity::Percentage == std::get(this->fieldType)) { - symbol = " %"; - } - if (amount) { - this->QLineEdit::setText( - Measurement::displayQuantity(*amount, precision.value_or(this->defaultPrecision)) + symbol - ); - } else { - this->QLineEdit::setText(""); - } - this->setDisplaySize(); - return; -} - -void BtLineEdit::setText(QString amount, std::optional precision) { - if (!amount.isEmpty() && NonPhysicalQuantity::String != std::get(this->fieldType)) { - bool ok = false; - double amt = Measurement::extractRawFromString(amount, &ok); - if (!ok) { - qWarning() << Q_FUNC_INFO << "Could not convert" << amount << "to double"; - } - this->setText(amt, precision.value_or(this->defaultPrecision)); - return; - } - - this->QLineEdit::setText(amount); - this->setDisplaySize(true); - return; -} - -void BtLineEdit::calculateDisplaySize(QString const & maximalDisplayString) { - // - // By default, some, but not all, boxes have a min and max width of 100 pixels, but this is not wide enough on a - // high DPI display. We instead calculate width here based on font-size - but without reducing any existing minimum - // width. - // - // Unfortunately, for a QLineEdit object, calculating the width is hard because, besides the text, we need to allow - // for the width of padding and frame, which is non-trivial to discover. Eg, typically: - // marginsAroundText() and contentsMargins() both return 0 for left and right margins - // contentsRect() and frameSize() both give the same width as width() - // AFAICT, the best option is to query via pixelMetric() calls to the widget's style, but we need to check this works - // in practice on a variety of different systems. - // - QFontMetrics displayFontMetrics(this->font()); - QRect minimumTextRect = displayFontMetrics.boundingRect(maximalDisplayString); - QMargins marginsAroundText = this->textMargins(); - auto myStyle = this->style(); - // NB: 2× frame width as on left and right; same for horizontal spacing - int totalWidgetWidthForMaximalDisplayString = minimumTextRect.width() + - marginsAroundText.left() + - marginsAroundText.right() + - (2 * myStyle->pixelMetric(QStyle::PM_DefaultFrameWidth)) + - (2 * myStyle->pixelMetric(QStyle::PM_LayoutHorizontalSpacing)); - - this->desiredWidthInPixels = qMax(this->minimumWidth(), totalWidgetWidthForMaximalDisplayString); - return; -} - -void BtLineEdit::setDisplaySize(bool recalculate) { - if ( recalculate ) { - QString sizing_string = text(); - - // this is a dirty bit of cheating. If we do not reset the minimum - // width, the field only ever gets bigger. This forces the resize I - // want, but only when we are instructed to force it - setMinimumWidth(0); - if ( sizing_string.length() < min_text_size ) { - sizing_string = QString(min_text_size,'a'); - } else if ( sizing_string.length() > max_text_size ) { - sizing_string = QString(max_text_size,'a'); - } - calculateDisplaySize(sizing_string); - } - this->setFixedWidth(this->desiredWidthInPixels); - return; -} - - -BtGenericEdit ::BtGenericEdit (QWidget *parent) : BtLineEdit(parent, NonPhysicalQuantity::String ) { return; } -BtStringEdit ::BtStringEdit (QWidget *parent) : BtLineEdit(parent, NonPhysicalQuantity::String ) { return; } -BtPercentageEdit ::BtPercentageEdit (QWidget *parent) : BtLineEdit(parent, NonPhysicalQuantity::Percentage , 0) { return; } -BtDimensionlessEdit::BtDimensionlessEdit(QWidget *parent) : BtLineEdit(parent, NonPhysicalQuantity::Dimensionless, 3) { return; } diff --git a/src/BtLineEdit.h b/src/BtLineEdit.h deleted file mode 100644 index eaa5ad84..00000000 --- a/src/BtLineEdit.h +++ /dev/null @@ -1,139 +0,0 @@ -/*====================================================================================================================== - * BtLineEdit.h is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Mark de Wever - * • Matt Young - * • Mike Evans - * • Mik Firestone - * • Philip Greggory Lee - * • Scott Peshak - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#ifndef BTLINEEDIT_H -#define BTLINEEDIT_H -#pragma once - -#include - -#include -#include -#include - -#include "BtFieldType.h" - -class NamedEntity; - -/*! - * \class BtLineEdit - * - * \brief This class and its subclasses extends QLineEdit such that the Object handles all the unit transformation we - * do, instead of each dialog. - * - * It makes the code much nicer and prevents more cut'n'paste code. - * - * A \c BtLineEdit (or subclass thereof) will usually have a corresponding \c BtLabel (or subclass thereof). - * See comment in BtLabel.h for more details on the relationship between the two classes. - */ -class BtLineEdit : public QLineEdit { - Q_OBJECT - -public: - /*! - * \brief Initialize the BtLineEdit with the parent and do some things with the type - * - * \param parent - QWidget* to the parent object - * \param fieldType the type of input field; if it is not \c NonPhysicalQuantity then we should be being called - * from \c BtAmountEdit or a subclass thereof - * \param defaultPrecision - * \param maximalDisplayString - an example of the widest string this widget would be expected to need to display - * - * \todo Not sure if I can get the name of the widget being created. - * Not sure how to signal the parent to redisplay - */ - BtLineEdit(QWidget* parent = nullptr, - BtFieldType fieldType = NonPhysicalQuantity::String, - int const defaultPrecision = 3, - QString const & maximalDisplayString = "100.000 srm"); - - virtual ~BtLineEdit(); - - BtFieldType const getFieldType() const; - - /** - * \brief Set the amount for a decimal field - * - * \param amount is the amount to display, but the field should be blank if this is \b std::nullopt - * \param precision is how many decimal places to show. If not specified, the default will be used. - */ - void setText(std::optional amount, std::optional precision = std::nullopt); - - /** - * \brief .:TBD:. Do we need this to be able to parse numbers out of strings, or just to set string text? - */ - void setText(QString amount, std::optional precision = std::nullopt); - - /** - * \brief Use this when you want to get the text as a number (and ignore any units or other trailling letters or - * symbols) - */ - template T getValueAs() const; - -public slots: - /** - * \brief This slot receives the \c QLineEdit::editingFinished signal - */ - void onLineChanged(); - -signals: - /** - * \brief Where we want "instant updates", this signal should be picked up by the editor or widget object using this - * input field so it can read the changed value and update the underlying data model. - * - * Where we want to defer updating the underlying data model until the user clicks "Save" etc, then this - * signal will typically be ignored. - */ - void textModified(); - -protected: - BtFieldType fieldType; - - int const defaultPrecision; - - void calculateDisplaySize(QString const & maximalDisplayString); - void setDisplaySize(bool recalculate = false); - - int desiredWidthInPixels; -}; - -// -// These are trivial specialisations of BtLineEdit that make it possible to use specific types of BtLineEdit in .ui -// files. It's a bit of a sledgehammer way to pass in a constructor parameter but seems necessary because of -// limitations in Qt. -// -// AFAIK there is no way to pass constructor parameters to an object in a .ui file. (If you want to do that, the advice -// seems to be to build the layout manually in C++ code.) -// -// Similarly, we might think to template BtLineEdit, but the Qt Meta-Object Compiler (moc) doesn't understand C++ -// templates. This means we can't template classes that need to use the Q_OBJECT macro (required for classes that -// declare their own signals and slots or that use other services provided by Qt's meta-object system). -// -// TODO: Kill BtGenericEdit -// -// TBD: Can we think of a more elegant way of handing, eg, different numbers of decimal places for % -// -class BtGenericEdit : public BtLineEdit { Q_OBJECT public: BtGenericEdit (QWidget* parent); }; -class BtStringEdit : public BtLineEdit { Q_OBJECT public: BtStringEdit (QWidget* parent); }; -class BtPercentageEdit : public BtLineEdit { Q_OBJECT public: BtPercentageEdit (QWidget* parent); }; -class BtDimensionlessEdit : public BtLineEdit { Q_OBJECT public: BtDimensionlessEdit(QWidget* parent); }; - -#endif diff --git a/src/BtTreeItem.cpp b/src/BtTreeItem.cpp index 252f31fc..1a6afc64 100644 --- a/src/BtTreeItem.cpp +++ b/src/BtTreeItem.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * BtTreeItem.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * BtTreeItem.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Daniel Pettersson * • Greg Meess * • Mattias Måhl @@ -48,16 +48,16 @@ namespace { EnumStringMapping const itemTypeToName { - {"RECIPE" , BtTreeItem::Type::RECIPE }, - {"EQUIPMENT" , BtTreeItem::Type::EQUIPMENT }, - {"FERMENTABLE", BtTreeItem::Type::FERMENTABLE}, - {"HOP" , BtTreeItem::Type::HOP }, - {"MISC" , BtTreeItem::Type::MISC }, - {"YEAST" , BtTreeItem::Type::YEAST }, - {"BREWNOTE" , BtTreeItem::Type::BREWNOTE }, - {"STYLE" , BtTreeItem::Type::STYLE }, - {"FOLDER" , BtTreeItem::Type::FOLDER }, - {"WATER" , BtTreeItem::Type::WATER } + {BtTreeItem::Type::RECIPE , "RECIPE" }, + {BtTreeItem::Type::EQUIPMENT , "EQUIPMENT" }, + {BtTreeItem::Type::FERMENTABLE, "FERMENTABLE"}, + {BtTreeItem::Type::HOP , "HOP" }, + {BtTreeItem::Type::MISC , "MISC" }, + {BtTreeItem::Type::YEAST , "YEAST" }, + {BtTreeItem::Type::BREWNOTE , "BREWNOTE" }, + {BtTreeItem::Type::STYLE , "STYLE" }, + {BtTreeItem::Type::FOLDER , "FOLDER" }, + {BtTreeItem::Type::WATER , "WATER" }, }; } @@ -283,10 +283,8 @@ QVariant BtTreeItem::dataFermentable(int column) { break; case FERMENTABLECOLORCOL: if (ferm) { - return QVariant(Measurement::displayAmount(Measurement::Amount{ferm->color_srm(), Measurement::Units::srm}, - BtString::EMPTY_STR, - PropertyNames::Fermentable::color_srm, - 0)); + return QVariant(Measurement::displayAmount(Measurement::Amount{ferm->color_srm(), + Measurement::Units::srm}, 0)); } break; default : @@ -332,12 +330,13 @@ QVariant BtTreeItem::dataMisc(int column) { } case MISCTYPECOL: if (misc) { - return QVariant(misc->typeStringTr()); + return QVariant(Misc::typeDisplayNames[misc->type()]); } break; case MISCUSECOL: if (misc) { - return QVariant(misc->useStringTr()); + auto const use = misc->use(); + return use ? QVariant(Misc::useDisplayNames[*use]) : ""; } break; default : diff --git a/src/BtTreeModel.cpp b/src/BtTreeModel.cpp index b95f3235..e68d7c42 100644 --- a/src/BtTreeModel.cpp +++ b/src/BtTreeModel.cpp @@ -528,6 +528,7 @@ bool BtTreeModel::removeRows(int row, int count, const QModelIndex & parent) { // One find method for all things. This .. is nice QModelIndex BtTreeModel::findElement(NamedEntity * thing, BtTreeItem * parent) { + qDebug() << Q_FUNC_INFO << "Find" << thing << "in" << parent; BtTreeItem * pItem; QList folders; @@ -551,6 +552,7 @@ QModelIndex BtTreeModel::findElement(NamedEntity * thing, BtTreeItem * parent) { for (i = 0; i < target->childCount(); ++i) { // If we've found what we are looking for, return if (target->child(i)->thing() == thing) { + qDebug() << Q_FUNC_INFO << "Found at" << i; return createIndex(i, 0, target->child(i)); } diff --git a/src/BtTreeView.cpp b/src/BtTreeView.cpp index 8a51b438..712f57ff 100644 --- a/src/BtTreeView.cpp +++ b/src/BtTreeView.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * BtTreeView.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * BtTreeView.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Mattias Måhl * • Matt Young @@ -32,10 +32,13 @@ #include "BtFolder.h" #include "BtTreeFilterProxyModel.h" #include "BtTreeModel.h" -#include "EquipmentEditor.h" -#include "FermentableDialog.h" -#include "HopDialog.h" -#include "MiscDialog.h" +#include "editors/EquipmentEditor.h" +#include "editors/StyleEditor.h" +#include "editors/WaterEditor.h" +#include "ingredientDialogs/FermentableDialog.h" +#include "ingredientDialogs/HopDialog.h" +#include "ingredientDialogs/MiscDialog.h" +#include "ingredientDialogs/YeastDialog.h" #include "model/BrewNote.h" #include "model/Equipment.h" #include "model/Fermentable.h" @@ -45,9 +48,6 @@ #include "model/Style.h" #include "model/Water.h" #include "model/Yeast.h" -#include "StyleEditor.h" -#include "WaterEditor.h" -#include "YeastDialog.h" BtTreeView::BtTreeView(QWidget * parent, BtTreeModel::TypeMasks type) : QTreeView{parent}, @@ -321,19 +321,19 @@ void BtTreeView::newNamedEntity() { qobject_cast(m_editor)->newEquipment(folder); break; case BtTreeModel::FERMENTMASK: - qobject_cast(m_editor)->newFermentable(folder); + qobject_cast(m_editor)->newItem(folder); break; case BtTreeModel::HOPMASK: - qobject_cast(m_editor)->newHop(folder); + qobject_cast(m_editor)->newItem(folder); break; case BtTreeModel::MISCMASK: - qobject_cast(m_editor)->newMisc(folder); + qobject_cast(m_editor)->newItem(folder); break; case BtTreeModel::STYLEMASK: qobject_cast(m_editor)->newStyle(folder); break; case BtTreeModel::YEASTMASK: - qobject_cast(m_editor)->newYeast(folder); + qobject_cast(m_editor)->newItem(folder); break; case BtTreeModel::WATERMASK: qobject_cast(m_editor)->newWater(folder); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 19adbc74..71d166bd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,13 +48,10 @@ set(filesToCompile_cpp ${repoDir}/src/BrewDayFormatter.cpp ${repoDir}/src/BrewDayScrollWidget.cpp ${repoDir}/src/BrewNoteWidget.cpp - ${repoDir}/src/BtAmountEdit.cpp ${repoDir}/src/BtColor.cpp ${repoDir}/src/BtDatePopup.cpp ${repoDir}/src/BtFieldType.cpp ${repoDir}/src/BtFolder.cpp - ${repoDir}/src/BtLabel.cpp - ${repoDir}/src/BtLineEdit.cpp ${repoDir}/src/BtSplashScreen.cpp ${repoDir}/src/BtTabWidget.cpp ${repoDir}/src/BtTextEdit.cpp @@ -63,28 +60,36 @@ set(filesToCompile_cpp ${repoDir}/src/BtTreeModel.cpp ${repoDir}/src/BtTreeView.cpp ${repoDir}/src/ConverterTool.cpp - ${repoDir}/src/CustomComboBox.cpp ${repoDir}/src/database/BtSqlQuery.cpp ${repoDir}/src/database/Database.cpp ${repoDir}/src/database/DatabaseSchemaHelper.cpp ${repoDir}/src/database/DbTransaction.cpp ${repoDir}/src/database/ObjectStore.cpp ${repoDir}/src/database/ObjectStoreTyped.cpp + ${repoDir}/src/editors/EquipmentEditor.cpp + ${repoDir}/src/editors/FermentableEditor.cpp + ${repoDir}/src/editors/HopEditor.cpp + ${repoDir}/src/editors/MashEditor.cpp + ${repoDir}/src/editors/MashStepEditor.cpp + ${repoDir}/src/editors/MiscEditor.cpp + ${repoDir}/src/editors/NamedMashEditor.cpp + ${repoDir}/src/editors/StyleEditor.cpp + ${repoDir}/src/editors/WaterEditor.cpp + ${repoDir}/src/editors/YeastEditor.cpp ${repoDir}/src/EquipmentButton.cpp - ${repoDir}/src/EquipmentEditor.cpp ${repoDir}/src/EquipmentListModel.cpp - ${repoDir}/src/FermentableDialog.cpp - ${repoDir}/src/FermentableEditor.cpp ${repoDir}/src/FermentableSortFilterProxyModel.cpp ${repoDir}/src/HeatCalculations.cpp ${repoDir}/src/HelpDialog.cpp - ${repoDir}/src/HopDialog.cpp - ${repoDir}/src/HopEditor.cpp ${repoDir}/src/HopSortFilterProxyModel.cpp ${repoDir}/src/Html.cpp ${repoDir}/src/HydrometerTool.cpp ${repoDir}/src/IbuGuSlider.cpp ${repoDir}/src/ImportExport.cpp + ${repoDir}/src/ingredientDialogs/FermentableDialog.cpp + ${repoDir}/src/ingredientDialogs/HopDialog.cpp + ${repoDir}/src/ingredientDialogs/MiscDialog.cpp + ${repoDir}/src/ingredientDialogs/YeastDialog.cpp ${repoDir}/src/InstructionWidget.cpp ${repoDir}/src/InventoryFormatter.cpp ${repoDir}/src/json/BeerJson.cpp @@ -99,11 +104,8 @@ set(filesToCompile_cpp ${repoDir}/src/Logging.cpp ${repoDir}/src/MainWindow.cpp ${repoDir}/src/MashButton.cpp - ${repoDir}/src/MashComboBox.cpp ${repoDir}/src/MashDesigner.cpp - ${repoDir}/src/MashEditor.cpp ${repoDir}/src/MashListModel.cpp - ${repoDir}/src/MashStepEditor.cpp ${repoDir}/src/MashStepTableWidget.cpp ${repoDir}/src/MashWizard.cpp ${repoDir}/src/matrix.cpp @@ -117,8 +119,6 @@ set(filesToCompile_cpp ${repoDir}/src/measurement/SystemOfMeasurement.cpp ${repoDir}/src/measurement/Unit.cpp ${repoDir}/src/measurement/UnitSystem.cpp - ${repoDir}/src/MiscDialog.cpp - ${repoDir}/src/MiscEditor.cpp ${repoDir}/src/MiscSortFilterProxyModel.cpp ${repoDir}/src/model/BrewNote.cpp ${repoDir}/src/model/Equipment.cpp @@ -138,7 +138,6 @@ set(filesToCompile_cpp ${repoDir}/src/model/Water.cpp ${repoDir}/src/model/Yeast.cpp ${repoDir}/src/NamedEntitySortProxyModel.cpp - ${repoDir}/src/NamedMashEditor.cpp ${repoDir}/src/OgAdjuster.cpp ${repoDir}/src/OptionDialog.cpp ${repoDir}/src/PersistentSettings.cpp @@ -152,10 +151,8 @@ set(filesToCompile_cpp ${repoDir}/src/RecipeFormatter.cpp ${repoDir}/src/RefractoDialog.cpp ${repoDir}/src/ScaleRecipeTool.cpp - ${repoDir}/src/SimpleUndoableUpdate.cpp ${repoDir}/src/StrikeWaterDialog.cpp ${repoDir}/src/StyleButton.cpp - ${repoDir}/src/StyleEditor.cpp ${repoDir}/src/StyleListModel.cpp ${repoDir}/src/StyleRangeWidget.cpp ${repoDir}/src/StyleSortFilterProxyModel.cpp @@ -171,25 +168,34 @@ set(filesToCompile_cpp ${repoDir}/src/TimerListDialog.cpp ${repoDir}/src/TimerMainDialog.cpp ${repoDir}/src/TimerWidget.cpp - ${repoDir}/src/UiAmountWithUnits.cpp + ${repoDir}/src/undoRedo/SimpleUndoableUpdate.cpp ${repoDir}/src/utils/BtException.cpp ${repoDir}/src/utils/BtStringConst.cpp ${repoDir}/src/utils/BtStringStream.cpp ${repoDir}/src/utils/EnumStringMapping.cpp ${repoDir}/src/utils/ImportRecordCount.cpp + ${repoDir}/src/utils/MetaTypes.cpp + ${repoDir}/src/utils/OptionalHelpers.cpp ${repoDir}/src/utils/OStreamWriterForQFile.cpp ${repoDir}/src/utils/TimerUtils.cpp ${repoDir}/src/utils/TypeLookup.cpp ${repoDir}/src/WaterButton.cpp ${repoDir}/src/WaterDialog.cpp - ${repoDir}/src/WaterEditor.cpp ${repoDir}/src/WaterListModel.cpp ${repoDir}/src/WaterSortFilterProxyModel.cpp - ${repoDir}/src/WaterTableWidget.cpp ${repoDir}/src/widgets/Animator.cpp - ${repoDir}/src/widgets/BtAmountDigitWidget.cpp - ${repoDir}/src/widgets/BtDigitWidget.cpp + ${repoDir}/src/widgets/BtBoolComboBox.cpp + ${repoDir}/src/widgets/BtComboBox.cpp + ${repoDir}/src/widgets/CustomComboBox.cpp + ${repoDir}/src/widgets/MashComboBox.cpp ${repoDir}/src/widgets/SelectionControl.cpp + ${repoDir}/src/widgets/SmartAmounts.cpp + ${repoDir}/src/widgets/SmartAmountSettings.cpp + ${repoDir}/src/widgets/SmartCheckBox.cpp + ${repoDir}/src/widgets/SmartDigitWidget.cpp + ${repoDir}/src/widgets/SmartField.cpp + ${repoDir}/src/widgets/SmartLabel.cpp + ${repoDir}/src/widgets/SmartLineEdit.cpp ${repoDir}/src/widgets/ToggleSwitch.cpp ${repoDir}/src/widgets/UnitAndScalePopUpMenu.cpp ${repoDir}/src/xml/BeerXml.cpp @@ -200,7 +206,5 @@ set(filesToCompile_cpp ${repoDir}/src/xml/XmlMashStepRecord.cpp ${repoDir}/src/xml/XmlRecipeRecord.cpp ${repoDir}/src/xml/XmlRecord.cpp - ${repoDir}/src/YeastDialog.cpp - ${repoDir}/src/YeastEditor.cpp ${repoDir}/src/YeastSortFilterProxyModel.cpp ) diff --git a/src/FermentableDialog.cpp b/src/FermentableDialog.cpp deleted file mode 100644 index e703e28b..00000000 --- a/src/FermentableDialog.cpp +++ /dev/null @@ -1,229 +0,0 @@ -/*====================================================================================================================== - * FermentableDialog.cpp is part of Brewken, and is copyright the following authors 2009-2021: - * • Brian Rower - * • Daniel Pettersson - * • Matt Young - * • Mik Firestone - * • Philip Greggory Lee - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "FermentableDialog.h" - -#include -#include -#include -#include -#include - -//#include "database/Database.h" -#include "database/ObjectStoreWrapper.h" -#include "FermentableEditor.h" -#include "FermentableSortFilterProxyModel.h" -#include "MainWindow.h" -#include "model/Fermentable.h" -#include "model/Recipe.h" -#include "tableModels/FermentableTableModel.h" - - -FermentableDialog::FermentableDialog(MainWindow* parent) : - QDialog(parent), - mainWindow(parent), - fermEdit(new FermentableEditor(this)), - numFerms(0) -{ - doLayout(); - - fermTableModel = new FermentableTableModel(tableWidget, false); - fermTableModel->setInventoryEditable(true); - fermTableProxy = new FermentableSortFilterProxyModel(tableWidget); - fermTableProxy->setSourceModel(fermTableModel); - tableWidget->setModel(fermTableProxy); - tableWidget->setSortingEnabled(true); - tableWidget->sortByColumn( FERMNAMECOL, Qt::AscendingOrder ); - fermTableProxy->setDynamicSortFilter(true); - fermTableProxy->setFilterKeyColumn(1); - - connect( pushButton_addToRecipe, SIGNAL( clicked() ), this, SLOT( addFermentable() ) ); - connect( pushButton_edit, &QAbstractButton::clicked, this, &FermentableDialog::editSelected ); - connect( pushButton_remove, &QAbstractButton::clicked, this, &FermentableDialog::removeFermentable ); - connect( pushButton_new, SIGNAL( clicked() ), this, SLOT( newFermentable() ) ); - connect( tableWidget, &QAbstractItemView::doubleClicked, this, &FermentableDialog::addFermentable ); - connect( qLineEdit_searchBox, &QLineEdit::textEdited, this, &FermentableDialog::filterFermentables); - // Let me see if this works - fermTableModel->observeDatabase(true); -} - -void FermentableDialog::doLayout() -{ - resize(800, 300); - verticalLayout = new QVBoxLayout(this); - tableWidget = new QTableView(this); - horizontalLayout = new QHBoxLayout(); - qLineEdit_searchBox = new QLineEdit(); - qLineEdit_searchBox->setMaxLength(30); - qLineEdit_searchBox->setPlaceholderText("Enter filter"); - horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - pushButton_addToRecipe = new QPushButton(this); - pushButton_addToRecipe->setObjectName(QStringLiteral("pushButton_addToRecipe")); - pushButton_addToRecipe->setAutoDefault(false); - pushButton_addToRecipe->setDefault(true); - pushButton_new = new QPushButton(this); - pushButton_new->setObjectName(QStringLiteral("pushButton_new")); - pushButton_new->setAutoDefault(false); - pushButton_edit = new QPushButton(this); - pushButton_edit->setObjectName(QStringLiteral("pushButton_edit")); - QIcon icon; - icon.addFile(QStringLiteral(":/images/edit.svg"), QSize(), QIcon::Normal, QIcon::Off); - pushButton_edit->setIcon(icon); - pushButton_edit->setAutoDefault(false); - pushButton_remove = new QPushButton(this); - pushButton_remove->setObjectName(QStringLiteral("pushButton_remove")); - QIcon icon1; - icon1.addFile(QStringLiteral(":/images/smallMinus.svg"), QSize(), QIcon::Normal, QIcon::Off); - pushButton_remove->setIcon(icon1); - pushButton_remove->setAutoDefault(false); - horizontalLayout->addWidget(qLineEdit_searchBox); - horizontalLayout->addItem(horizontalSpacer); - horizontalLayout->addWidget(pushButton_addToRecipe); - horizontalLayout->addWidget(pushButton_new); - horizontalLayout->addWidget(pushButton_edit); - horizontalLayout->addWidget(pushButton_remove); - verticalLayout->addWidget(tableWidget); - verticalLayout->addLayout(horizontalLayout); - - retranslateUi(); - QMetaObject::connectSlotsByName(this); -} - -void FermentableDialog::retranslateUi() -{ - setWindowTitle(tr("Fermentable Database")); - pushButton_addToRecipe->setText(tr("Add to Recipe")); - pushButton_new->setText(tr("New")); - pushButton_edit->setText(QString()); - pushButton_remove->setText(QString()); -#ifndef QT_NO_TOOLTIP - pushButton_addToRecipe->setToolTip(tr("Add selected ingredient to recipe")); - pushButton_new->setToolTip(tr("Create new ingredient")); - pushButton_edit->setToolTip(tr("Edit selected ingredient")); - pushButton_remove->setToolTip(tr("Remove selected ingredient")); -#endif // QT_NO_TOOLTIP -} - -void FermentableDialog::removeFermentable() { - QModelIndexList selected = tableWidget->selectionModel()->selectedIndexes(); - - int size = selected.size(); - if (size == 0) { - return; - } - - // Make sure only one row is selected. - int row = selected[0].row(); - for (int i = 1; i < size; ++i) { - if (selected[i].row() != row) { - return; - } - } - - QModelIndex translated = fermTableProxy->mapToSource(selected[0]); - auto ferm = fermTableModel->getRow(translated.row()); - ObjectStoreWrapper::softDelete(*ferm); - return; -} - -void FermentableDialog::editSelected() { - QModelIndexList selected = tableWidget->selectionModel()->selectedIndexes(); - - int size = selected.size(); - if (size == 0) { - return; - } - - // Make sure only one row is selected. - int row = selected[0].row(); - for (int i = 1; i < size; ++i) { - if (selected[i].row() != row) { - return; - } - } - - QModelIndex translated = fermTableProxy->mapToSource(selected[0]); - auto ferm = fermTableModel->getRow(translated.row()); - fermEdit->setFermentable(ferm.get()); - fermEdit->show(); - return; -} - -void FermentableDialog::addFermentable(const QModelIndex& index) { - QModelIndex translated; - - // If there is no provided index, get the selected index. - if (!index.isValid()) { - QModelIndexList selected = tableWidget->selectionModel()->selectedIndexes(); - - int size = selected.size(); - if (size == 0) { - return; - } - - // Make sure only one row is selected. - int row = selected[0].row(); - for (int i = 1; i < size; ++i) { - if (selected[i].row() != row) { - return; - } - } - - translated = fermTableProxy->mapToSource(selected[0]); - } - else - { - // Only respond if the name is selected. Since we connect to double-click signal, - // this keeps us from adding something to the recipe when we just want to edit - // one of the other fermentable fields. - if( index.column() == FERMNAMECOL ) - translated = fermTableProxy->mapToSource(index); - else - return; - } - - MainWindow::instance().addFermentableToRecipe(fermTableModel->getRow(translated.row())); - - return; -} - -void FermentableDialog::newFermentable(QString folder) -{ - QString name = QInputDialog::getText(this, tr("Fermentable name"), - tr("Fermentable name:")); - if( name.isEmpty() ) - return; - - Fermentable* ferm = new Fermentable(name); - if ( ! folder.isEmpty() ) - ferm->setFolder(folder); - - fermEdit->setFermentable(ferm); - fermEdit->show(); -} - -void FermentableDialog::newFermentable() { - newFermentable(QString()); -} - -void FermentableDialog::filterFermentables(QString searchExpression) -{ - fermTableProxy->setFilterCaseSensitivity(Qt::CaseInsensitive); - fermTableProxy->setFilterFixedString(searchExpression); -} diff --git a/src/FermentableDialog.h b/src/FermentableDialog.h deleted file mode 100644 index a44632c2..00000000 --- a/src/FermentableDialog.h +++ /dev/null @@ -1,96 +0,0 @@ -/*====================================================================================================================== - * FermentableDialog.h is part of Brewken, and is copyright the following authors 2009-2015: - * • Daniel Pettersson - * • Jeff Bailey - * • Mik Firestone - * • Philip Greggory Lee - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#ifndef FERMENTABLEDIALOG_H -#define FERMENTABLEDIALOG_H -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -// Forward declarations. -class MainWindow; -class FermentableEditor; -class FermentableTableModel; -class FermentableSortFilterProxyModel; - -/*! - * \class FermentableDialog - * - * \brief View/controller class that shows the list of fermentables in the database. - */ -class FermentableDialog : public QDialog { - Q_OBJECT - -public: - FermentableDialog(MainWindow* parent); - virtual ~FermentableDialog() {} - - //! \name Public UI Variables - //! @{ - QVBoxLayout *verticalLayout; - QTableView *tableWidget; - QHBoxLayout *horizontalLayout; - QLineEdit *qLineEdit_searchBox; - QSpacerItem *horizontalSpacer; - QPushButton *pushButton_addToRecipe; - QPushButton *pushButton_new; - QPushButton *pushButton_edit; - QPushButton *pushButton_remove; - //! @} - - void newFermentable(QString folder); -public slots: - /*! If \b index is the default, will add the selected fermentable to list. - * Otherwise, will add the fermentable at the specified index. - */ - void addFermentable(const QModelIndex& index = QModelIndex()); - void removeFermentable(); - void editSelected(); - - void filterFermentables(QString searchExpression); - //void changed(QMetaProperty,QVariant); - void newFermentable(); - -protected: - - virtual void changeEvent(QEvent* event) - { - if(event->type() == QEvent::LanguageChange) - retranslateUi(); - QDialog::changeEvent(event); - } - -private: - MainWindow* mainWindow; - FermentableTableModel* fermTableModel; - FermentableSortFilterProxyModel* fermTableProxy; - FermentableEditor* fermEdit; - int numFerms; - - void doLayout(); - void retranslateUi(); -}; - -#endif diff --git a/src/FermentableEditor.cpp b/src/FermentableEditor.cpp deleted file mode 100644 index d22fc0c4..00000000 --- a/src/FermentableEditor.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/*====================================================================================================================== - * FermentableEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Daniel Pettersson - * • Kregg Kemper - * • Matt Young - * • Mik Firestone - * • Philip Greggory Lee - * • Samuel Östling - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "FermentableEditor.h" - -#include -#include - -#include "BtHorizontalTabs.h" -#include "database/ObjectStoreWrapper.h" -#include "measurement/Unit.h" -#include "model/Fermentable.h" - -FermentableEditor::FermentableEditor(QWidget* parent) : - QDialog(parent), - obsFerm(nullptr) { - setupUi(this); - - this->tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs); - - // See comment in HopEditor.cpp about combo box setup in Qt - for (auto ii : Fermentable::allTypes) { - this->comboBox_fermentableType->addItem(Fermentable::typeDisplayNames[ii], - Fermentable::typeStringMapping.enumToString(ii)); - } - - connect(pushButton_new, &QAbstractButton::clicked, this, &FermentableEditor::clickedNewFermentable); - connect(pushButton_save, &QAbstractButton::clicked, this, &FermentableEditor::save); - connect(pushButton_cancel, &QAbstractButton::clicked, this, &FermentableEditor::clearAndClose); - return; -} - -FermentableEditor::~FermentableEditor() = default; - -void FermentableEditor::setFermentable(Fermentable * newFerm) { - if (newFerm) { - obsFerm = newFerm; - showChanges(); - } - return; -} - -void FermentableEditor::save() { - if (!obsFerm) { - setVisible(false); - return; - } - - obsFerm->setName(lineEdit_name->text()); - - // - // 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->obsFerm->setType( - Fermentable::typeStringMapping.stringToEnum(comboBox_fermentableType->currentData().toString()) - ); - - obsFerm->setYield_pct (lineEdit_yield ->getValueAs()); - obsFerm->setColor_srm (lineEdit_color ->toCanonical().quantity()); - obsFerm->setAddAfterBoil (checkBox_addAfterBoil ->checkState() == Qt::Checked); - obsFerm->setOrigin (lineEdit_origin ->text()); - obsFerm->setSupplier (lineEdit_supplier ->text()); - obsFerm->setCoarseFineDiff_pct (lineEdit_coarseFineDiff->getValueAs()); - obsFerm->setMoisture_pct (lineEdit_moisture ->getValueAs()); - obsFerm->setDiastaticPower_lintner(lineEdit_diastaticPower->toCanonical().quantity()); - obsFerm->setProtein_pct (lineEdit_protein ->getValueAs()); - obsFerm->setMaxInBatch_pct (lineEdit_maxInBatch ->getValueAs()); - obsFerm->setRecommendMash (checkBox_recommendMash ->checkState() == Qt::Checked); - obsFerm->setIsMashed (checkBox_isMashed ->checkState() == Qt::Checked); - obsFerm->setIbuGalPerLb (lineEdit_ibuGalPerLb ->getValueAs()); // .:TBD:. No metric measure? - obsFerm->setNotes (textEdit_notes ->toPlainText()); - - if (this->obsFerm->key() < 0) { - ObjectStoreWrapper::insert(*this->obsFerm); - } - - // 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->toCanonical().quantity()); - - setVisible(false); - return; -} - -void FermentableEditor::clearAndClose() { - setFermentable(nullptr); - setVisible(false); // Hide the window. -} - -void FermentableEditor::showChanges(QMetaProperty* metaProp) { - if (!this->obsFerm) { - return; - } - - QString propName; - bool updateAll = false; - if (metaProp == nullptr) { - updateAll = true; - } else { - propName = metaProp->name(); - } - - if (propName == PropertyNames::Fermentable::type || updateAll) { - // As above, it's a coding error if there isn't a combo box entry corresponding to the Hop type - comboBox_fermentableType->setCurrentIndex( - comboBox_fermentableType->findData(Fermentable::typeStringMapping.enumToString(obsFerm->type())) - ); - if (!updateAll) { - return; - } - } - if (updateAll || propName == PropertyNames::NamedEntity::name ) { lineEdit_name ->setText(obsFerm->name() ); // Continues to next line - lineEdit_name->setCursorPosition(0); tabWidget_editor->setTabText(0, obsFerm->name()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::NamedEntityWithInventory::inventory) { lineEdit_inventory ->setText(obsFerm->inventory() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::yield_pct ) { lineEdit_yield ->setText(obsFerm->yield_pct() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::color_srm ) { lineEdit_color ->setText(obsFerm->color_srm(), 0 ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::addAfterBoil ) { checkBox_addAfterBoil ->setCheckState(obsFerm->addAfterBoil() ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::origin ) { lineEdit_origin ->setText(obsFerm->origin() ); lineEdit_origin ->setCursorPosition(0); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::supplier ) { lineEdit_supplier ->setText(obsFerm->supplier() ); lineEdit_supplier->setCursorPosition(0); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::coarseFineDiff_pct ) { lineEdit_coarseFineDiff->setText(obsFerm->coarseFineDiff_pct() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::moisture_pct ) { lineEdit_moisture ->setText(obsFerm->moisture_pct() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::diastaticPower_lintner) { lineEdit_diastaticPower->setText(obsFerm->diastaticPower_lintner()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::protein_pct ) { lineEdit_protein ->setText(obsFerm->protein_pct() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::maxInBatch_pct ) { lineEdit_maxInBatch ->setText(obsFerm->maxInBatch_pct() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::recommendMash ) { checkBox_recommendMash ->setCheckState(obsFerm->recommendMash() ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::isMashed ) { checkBox_isMashed ->setCheckState(obsFerm->isMashed() ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::ibuGalPerLb ) { lineEdit_ibuGalPerLb ->setText(obsFerm->ibuGalPerLb() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Fermentable::notes ) { textEdit_notes ->setPlainText(obsFerm->notes() ); if (!updateAll) { return; } } - return; -} - -void FermentableEditor::newFermentable(QString folder) { - QString name = QInputDialog::getText(this, tr("Fermentable name"), tr("Fermentable name:")); - 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()) { - f->setFolder(folder); - } - - setFermentable(f); - show(); - return; -} - -void FermentableEditor::clickedNewFermentable() { - newFermentable(QString()); - return; -} diff --git a/src/FermentableSortFilterProxyModel.cpp b/src/FermentableSortFilterProxyModel.cpp index 1518c14d..aa569b2b 100644 --- a/src/FermentableSortFilterProxyModel.cpp +++ b/src/FermentableSortFilterProxyModel.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * FermentableSortFilterProxyModel.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * FermentableSortFilterProxyModel.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Daniel Pettersson * • Jamie Daws * • Matt Young @@ -42,8 +42,9 @@ bool FermentableSortFilterProxyModel::lessThan(QModelIndex const & left, QVariant leftFermentable = sourceModel()->data(left); QVariant rightFermentable = sourceModel()->data(right); - switch (left.column()) { - case FERMINVENTORYCOL: + auto const columnIndex = static_cast(left.column()); + switch (columnIndex) { + case FermentableTableModel::ColumnIndex::Inventory: // If the numbers are equal, compare the names and be done with it if (Measurement::qStringToSI(leftFermentable.toString(), Measurement::PhysicalQuantity::Mass) == Measurement::qStringToSI(rightFermentable.toString(), Measurement::PhysicalQuantity::Mass)) { @@ -57,7 +58,7 @@ bool FermentableSortFilterProxyModel::lessThan(QModelIndex const & left, return Measurement::qStringToSI(leftFermentable.toString(), Measurement::PhysicalQuantity::Mass) < Measurement::qStringToSI(rightFermentable.toString(), Measurement::PhysicalQuantity::Mass); - case FERMAMOUNTCOL: + case FermentableTableModel::ColumnIndex::Amount: // If the numbers are equal, compare the names and be done with it if (Measurement::qStringToSI(leftFermentable.toString(), Measurement::PhysicalQuantity::Mass) == Measurement::qStringToSI(rightFermentable.toString(), Measurement::PhysicalQuantity::Mass)) { @@ -66,7 +67,7 @@ bool FermentableSortFilterProxyModel::lessThan(QModelIndex const & left, return Measurement::qStringToSI(leftFermentable.toString(), Measurement::PhysicalQuantity::Mass) < Measurement::qStringToSI(rightFermentable.toString(), Measurement::PhysicalQuantity::Mass); - case FERMYIELDCOL: + case FermentableTableModel::ColumnIndex::Yield: { double leftDouble = toDouble(leftFermentable); double rightDouble = toDouble(rightFermentable); @@ -77,7 +78,7 @@ bool FermentableSortFilterProxyModel::lessThan(QModelIndex const & left, return leftDouble < rightDouble; } - case FERMCOLORCOL: + case FermentableTableModel::ColumnIndex::Color: { auto leftAmount = Measurement::qStringToSI(leftFermentable.toString(), Measurement::PhysicalQuantity::Color); @@ -88,6 +89,14 @@ bool FermentableSortFilterProxyModel::lessThan(QModelIndex const & left, } return leftAmount < rightAmount; } + + case FermentableTableModel::ColumnIndex::Name : + case FermentableTableModel::ColumnIndex::Type : + case FermentableTableModel::ColumnIndex::IsWeight : + case FermentableTableModel::ColumnIndex::IsMashed : + case FermentableTableModel::ColumnIndex::AfterBoil: + // Nothing to do for these cases + break; } return leftFermentable.toString() < rightFermentable.toString(); @@ -97,14 +106,15 @@ double FermentableSortFilterProxyModel::toDouble(QVariant side) const { return Localization::toDouble(side.toString(), Q_FUNC_INFO); } -QString FermentableSortFilterProxyModel::getName( const QModelIndex &index ) const -{ - QVariant info = sourceModel()->data(QAbstractItemModel::createIndex(index.row(),FERMNAMECOL)); +QString FermentableSortFilterProxyModel::getName( const QModelIndex &index ) const { + QVariant info = sourceModel()->data( + QAbstractItemModel::createIndex(index.row(), + static_cast(FermentableTableModel::ColumnIndex::Name))) + ; return info.toString(); } -bool FermentableSortFilterProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent) const -{ +bool FermentableSortFilterProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent) const { FermentableTableModel* model = qobject_cast(sourceModel()); QModelIndex index = sourceModel()->index(source_row, 0, source_parent); diff --git a/src/HopDialog.cpp b/src/HopDialog.cpp deleted file mode 100644 index f82c7a2d..00000000 --- a/src/HopDialog.cpp +++ /dev/null @@ -1,236 +0,0 @@ -/*====================================================================================================================== - * HopDialog.cpp is part of Brewken, and is copyright the following authors 2009-2021: - * • Brian Rower - * • Daniel Pettersson - * • Luke Vincent - * • Markus Mårtensson - * • Matt Young - * • Mik Firestone - * • Philip Greggory Lee - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "HopDialog.h" - -#include -#include -#include -#include -#include - -//#include "database/Database.h" -#include "database/ObjectStoreWrapper.h" -#include "HopEditor.h" -#include "HopSortFilterProxyModel.h" -#include "MainWindow.h" -#include "model/Hop.h" -#include "model/Recipe.h" -#include "tableModels/HopTableModel.h" - -HopDialog::HopDialog(MainWindow* parent) : - QDialog(parent), - mainWindow(parent), - hopEditor(new HopEditor(this)), - numHops(0) -{ - doLayout(); - - hopTableModel = new HopTableModel(tableWidget, false); - hopTableModel->setInventoryEditable(true); - hopTableProxy = new HopSortFilterProxyModel(tableWidget); - hopTableProxy->setSourceModel(hopTableModel); - tableWidget->setModel(hopTableProxy); - tableWidget->setSortingEnabled(true); - tableWidget->sortByColumn( HOPNAMECOL, Qt::AscendingOrder ); - hopTableProxy->setDynamicSortFilter(true); - hopTableProxy->setFilterKeyColumn(1); - - connect( pushButton_addToRecipe, SIGNAL( clicked() ), this, SLOT( addHop() ) ); - connect( pushButton_edit, &QAbstractButton::clicked, this, &HopDialog::editSelected ); - connect( pushButton_new, SIGNAL( clicked() ), this, SLOT( newHop() ) ); - connect( pushButton_remove, &QAbstractButton::clicked, this, &HopDialog::removeHop); - connect( tableWidget, &QAbstractItemView::doubleClicked, this, &HopDialog::addHop ); - connect( qLineEdit_searchBox, &QLineEdit::textEdited, this, &HopDialog::filterHops); - - hopTableModel->observeDatabase(true); -} - -void HopDialog::doLayout() -{ - resize(800, 300); - verticalLayout = new QVBoxLayout(this); - tableWidget = new QTableView(this); - horizontalLayout = new QHBoxLayout(); - qLineEdit_searchBox = new QLineEdit(); - qLineEdit_searchBox->setMaxLength(30); - qLineEdit_searchBox->setPlaceholderText("Enter filter"); - horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - pushButton_addToRecipe = new QPushButton(this); - pushButton_addToRecipe->setObjectName(QStringLiteral("pushButton_addToRecipe")); - pushButton_addToRecipe->setAutoDefault(false); - pushButton_addToRecipe->setDefault(true); - pushButton_new = new QPushButton(this); - pushButton_new->setObjectName(QStringLiteral("pushButton_new")); - pushButton_new->setAutoDefault(false); - pushButton_edit = new QPushButton(this); - pushButton_edit->setObjectName(QStringLiteral("pushButton_edit")); - QIcon icon; - icon.addFile(QStringLiteral(":/images/edit.svg"), QSize(), QIcon::Normal, QIcon::Off); - pushButton_edit->setIcon(icon); - pushButton_edit->setAutoDefault(false); - pushButton_remove = new QPushButton(this); - pushButton_remove->setObjectName(QStringLiteral("pushButton_remove")); - QIcon icon1; - icon1.addFile(QStringLiteral(":/images/smallMinus.svg"), QSize(), QIcon::Normal, QIcon::Off); - pushButton_remove->setIcon(icon1); - pushButton_remove->setAutoDefault(false); - horizontalLayout->addWidget(qLineEdit_searchBox); - horizontalLayout->addItem(horizontalSpacer); - horizontalLayout->addWidget(pushButton_addToRecipe); - horizontalLayout->addWidget(pushButton_new); - horizontalLayout->addWidget(pushButton_edit); - horizontalLayout->addWidget(pushButton_remove); - verticalLayout->addWidget(tableWidget); - verticalLayout->addLayout(horizontalLayout); - - retranslateUi(); - QMetaObject::connectSlotsByName(this); -} - -void HopDialog::retranslateUi() -{ - setWindowTitle(tr("Hop Database")); - pushButton_addToRecipe->setText(tr("Add to Recipe")); - pushButton_new->setText(tr("New")); - pushButton_edit->setText(QString()); - pushButton_remove->setText(QString()); -#ifndef QT_NO_TOOLTIP - pushButton_addToRecipe->setToolTip(tr("Add selected ingredient to recipe")); - pushButton_new->setToolTip(tr("Create new ingredient")); - pushButton_edit->setToolTip(tr("Edit selected ingredient")); - pushButton_remove->setToolTip(tr("Remove selected ingredient")); -#endif // QT_NO_TOOLTIP -} - -void HopDialog::removeHop() { - QModelIndex modelIndex, viewIndex; - QModelIndexList selected = tableWidget->selectionModel()->selectedIndexes(); - int row, size, i; - - size = selected.size(); - if (size == 0) - return; - - // Make sure only one row is selected. - row = selected[0].row(); - for (i = 1; i < size; ++i) - { - if (selected[i].row() != row) - return; - } - modelIndex = hopTableProxy->mapToSource(selected[0]); - auto hop = hopTableModel->getRow(modelIndex.row()); - if (hop) { - ObjectStoreWrapper::softDelete(*hop); - } - return; -} - -void HopDialog::addHop(const QModelIndex& index) -{ - QModelIndex translated; - if( !index.isValid() ) - { - QModelIndexList selected = tableWidget->selectionModel()->selectedIndexes(); - int row, size, i; - - size = selected.size(); - if( size == 0 ) - return; - - // Make sure only one row is selected. - row = selected.value(0).row(); - for( i = 1; i < size; ++i ) - { - if( selected.value(i).row() != row ) - return; - } - - translated = hopTableProxy->mapToSource(selected.value(0)); - } - else - { - // Only respond if the name is selected. Since we connect to double-click signal, - // this keeps us from adding something to the recipe when we just want to edit - // one of the other columns. - if( index.column() == HOPNAMECOL ) - translated = hopTableProxy->mapToSource(index); - else - return; - } - - MainWindow::instance().addHopToRecipe(hopTableModel->getRow(translated.row())); - - return; -} - -void HopDialog::editSelected() -{ - QModelIndexList selected = tableWidget->selectionModel()->selectedIndexes(); - QModelIndex translated; - int row, size, i; - - size = selected.size(); - if( size == 0 ) - return; - - // Make sure only one row is selected. - row = selected.value(0).row(); - for( i = 1; i < size; ++i ) - { - if( selected.value(i).row() != row ) - return; - } - - translated = hopTableProxy->mapToSource(selected.value(0)); - auto hop = hopTableModel->getRow(translated.row()); - hopEditor->setHop(hop.get()); - hopEditor->show(); - return; -} - -void HopDialog::newHop() -{ - newHop(QString()); -} - -void HopDialog::newHop(QString folder) -{ - QString name = QInputDialog::getText(this, tr("Hop name"), - tr("Hop name:")); - if( name.isEmpty() ) - return; - - // .:TODO:. Change to shared_ptr as potential memory leak - Hop* hop = new Hop(name); - if ( ! folder.isEmpty() ) - hop->setFolder(folder); - - hopEditor->setHop(hop); - hopEditor->show(); -} - -void HopDialog::filterHops(QString searchExpression) -{ - hopTableProxy->setFilterCaseSensitivity(Qt::CaseInsensitive); - hopTableProxy->setFilterFixedString(searchExpression); -} diff --git a/src/HopEditor.cpp b/src/HopEditor.cpp deleted file mode 100644 index 9ef92a82..00000000 --- a/src/HopEditor.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/*====================================================================================================================== - * HopEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Kregg Kemper - * • Matt Young - * • Mik Firestone - * • Philip Greggory Lee - * • Samuel Östling - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "HopEditor.h" - -#include -#include -#include - -#include "BtHorizontalTabs.h" -#include "config.h" -#include "database/ObjectStoreWrapper.h" -#include "measurement/Unit.h" -#include "model/Hop.h" - -HopEditor::HopEditor(QWidget * parent) : - QDialog(parent), - obsHop(nullptr) { - setupUi(this); - - this->tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs); - - // - // According to https://bugreports.qt.io/browse/QTBUG-50823 it is never going to be possible to specify the data (as - // opposed to display text) for a combo box via the .ui file. So we have to do it in code instead. - // We could use the raw enum values as the data, but it would be a bit painful to debug if we ever had to, so for - // small extra effort we use the same serialisation strings that we use for BeerJSON and the DB. - // - for (auto ii : Hop::allTypes) { - this->comboBox_hopType->addItem(Hop::typeDisplayNames[ii], Hop::typeStringMapping.enumToString(ii)); - } - for (auto ii : Hop::allForms) { - this->comboBox_hopForm->addItem(Hop::formDisplayNames[ii], Hop::formStringMapping.enumToString(ii)); - } - for (auto ii : Hop::allUses) { - this->comboBox_hopUse->addItem (Hop::useDisplayNames[ii], Hop::useStringMapping.enumToString(ii)); - } - - connect(pushButton_new, &QAbstractButton::clicked, this, &HopEditor::clickedNewHop); - connect(pushButton_save, &QAbstractButton::clicked, this, &HopEditor::save); - connect(pushButton_cancel, &QAbstractButton::clicked, this, &HopEditor::clearAndClose); - - return; -} - -HopEditor::~HopEditor() = default; - -void HopEditor::setHop(Hop * h) { - if (obsHop) { - disconnect(obsHop, nullptr, this, nullptr); - } - - obsHop = h; - if (obsHop) { - connect(obsHop, &NamedEntity::changed, this, &HopEditor::changed); - showChanges(); - } - return; -} - -void HopEditor::save() { - if (!this->obsHop) { - setVisible(false); - return; - } - - this->obsHop->setName (lineEdit_name ->text()); - this->obsHop->setAlpha_pct (lineEdit_alpha ->getValueAs()); - this->obsHop->setTime_min (lineEdit_time ->toCanonical().quantity()); - this->obsHop->setBeta_pct (lineEdit_beta ->getValueAs()); - this->obsHop->setHsi_pct (lineEdit_HSI ->getValueAs()); - this->obsHop->setOrigin (lineEdit_origin ->text() ); - this->obsHop->setHumulene_pct (lineEdit_humulene ->getValueAs()); - this->obsHop->setCaryophyllene_pct(lineEdit_caryophyllene->getValueAs()); - this->obsHop->setCohumulone_pct (lineEdit_cohumulone ->getValueAs()); - this->obsHop->setMyrcene_pct (lineEdit_myrcene ->getValueAs()); - this->obsHop->setSubstitutes (textEdit_substitutes ->toPlainText() ); - this->obsHop->setNotes (textEdit_notes ->toPlainText() ); - - // - // It's a coding error if we don't recognise the values in our own combo boxes, so it's OK that we'd get a - // std::bad_optional_access exception in such a case - // - this->obsHop->setType(Hop::typeStringMapping.stringToEnum(comboBox_hopType->currentData().toString())); - this->obsHop->setForm(Hop::formStringMapping.stringToEnum(comboBox_hopForm->currentData().toString())); - this->obsHop->setUse (Hop::useStringMapping.stringToEnum (comboBox_hopUse->currentData().toString())); - - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - - - if (this->obsHop->key() < 0) { - ObjectStoreWrapper::insert(*this->obsHop); - } - - // do this late to make sure we've the row in the inventory table - this->obsHop->setInventoryAmount(lineEdit_inventory->toCanonical().quantity()); - setVisible(false); - return; -} - -void HopEditor::clearAndClose() { - setHop(nullptr); - setVisible(false); // Hide the window. -} - -void HopEditor::changed(QMetaProperty prop, QVariant /*val*/) { - if (sender() == obsHop) { - showChanges(&prop); - } -} - -void HopEditor::showChanges(QMetaProperty * prop) { - if (!this->obsHop) { - return; - } - - bool updateAll = false; - QString propName = ""; - - if (prop == nullptr) { - updateAll = true; - } else { - propName = prop->name(); - } - - if (updateAll || propName == PropertyNames::Hop::use) { - // As above, it's a coding error if there isn't a combo box entry corresponding to the Hop type - comboBox_hopUse->setCurrentIndex( - comboBox_hopUse->findData(Hop::useStringMapping.enumToString(obsHop->use())) - ); - if (!updateAll) { - return; - } - } - if (updateAll || propName == PropertyNames::Hop::type) { - // As above, it's a coding error if there isn't a combo box entry corresponding to the Hop type - comboBox_hopType->setCurrentIndex( - comboBox_hopType->findData(Hop::typeStringMapping.enumToString(obsHop->type())) - ); - if (!updateAll) { - return; - } - } - if (updateAll || propName == PropertyNames::Hop::form) { - // As above, it's a coding error if there isn't a combo box entry corresponding to the Hop form - comboBox_hopForm->setCurrentIndex( - comboBox_hopForm->findData(Hop::formStringMapping.enumToString(obsHop->form())) - ); - if (!updateAll) { - return; - } - } - if (updateAll || propName == PropertyNames::NamedEntity::name ) { lineEdit_name ->setText(obsHop->name ()); // Continues to next line - lineEdit_name ->setCursorPosition(0); tabWidget_editor->setTabText(0, obsHop->name()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::origin ) { lineEdit_origin ->setText(obsHop->origin ()); lineEdit_origin->setCursorPosition(0); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::alpha_pct ) { lineEdit_alpha ->setText(obsHop->alpha_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::time_min ) { lineEdit_time ->setText(obsHop->time_min ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::beta_pct ) { lineEdit_beta ->setText(obsHop->beta_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::hsi_pct ) { lineEdit_HSI ->setText(obsHop->hsi_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::humulene_pct ) { lineEdit_humulene ->setText(obsHop->humulene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::caryophyllene_pct ) { lineEdit_caryophyllene ->setText(obsHop->caryophyllene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::cohumulone_pct ) { lineEdit_cohumulone ->setText(obsHop->cohumulone_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::myrcene_pct ) { lineEdit_myrcene ->setText(obsHop->myrcene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::substitutes ) { textEdit_substitutes ->setPlainText(obsHop->substitutes ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::notes ) { textEdit_notes ->setPlainText(obsHop->notes ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::NamedEntityWithInventory::inventory) { lineEdit_inventory ->setText(obsHop->inventory ()); if (!updateAll) { return; } } - // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - if (updateAll || propName == PropertyNames::Hop::producer ) {lineEdit_producer ->setText(obsHop->producer ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::product_id ) {lineEdit_product_id ->setText(obsHop->product_id ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::year ) {lineEdit_year ->setText(obsHop->year ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::total_oil_ml_per_100g ) {lineEdit_total_oil_ml_per_100g->setText(obsHop->total_oil_ml_per_100g()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::farnesene_pct ) {lineEdit_farnesene ->setText(obsHop->farnesene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::geraniol_pct ) {lineEdit_geraniol ->setText(obsHop->geraniol_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::b_pinene_pct ) {lineEdit_b_pinene ->setText(obsHop->b_pinene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::linalool_pct ) {lineEdit_linalool ->setText(obsHop->linalool_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::limonene_pct ) {lineEdit_limonene ->setText(obsHop->limonene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::nerol_pct ) {lineEdit_nerol ->setText(obsHop->nerol_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::pinene_pct ) {lineEdit_pinene ->setText(obsHop->pinene_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::polyphenols_pct ) {lineEdit_polyphenols ->setText(obsHop->polyphenols_pct ()); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Hop::xanthohumol_pct ) {lineEdit_xanthohumol ->setText(obsHop->xanthohumol_pct ()); if (!updateAll) { return; } } - - return; -} - - -void HopEditor::newHop(QString folder) { - QString name = QInputDialog::getText(this, tr("Hop name"), tr("Hop name:")); - if (name.isEmpty()) { - return; - } - - // .:TODO:. Change this to shared_ptr as currently results in memory leak in clearAndClose() - Hop * h = new Hop(name); - - if (! folder.isEmpty()) { - h->setFolder(folder); - } - - setHop(h); - show(); - return; -} - -void HopEditor::clickedNewHop() { - newHop(QString()); - return; -} diff --git a/src/HopSortFilterProxyModel.cpp b/src/HopSortFilterProxyModel.cpp index a9a6d7db..0b25e5f8 100644 --- a/src/HopSortFilterProxyModel.cpp +++ b/src/HopSortFilterProxyModel.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * HopSortFilterProxyModel.cpp is part of Brewken, and is copyright the following authors 2009-2021: + * HopSortFilterProxyModel.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Daniel Pettersson * • Mattias Måhl * • Matt Young @@ -28,27 +28,29 @@ #include "model/Hop.h" #include "tableModels/HopTableModel.h" -HopSortFilterProxyModel::HopSortFilterProxyModel(QObject *parent, bool filt) -: QSortFilterProxyModel(parent) -{ - filter = filt; +HopSortFilterProxyModel::HopSortFilterProxyModel(QObject *parent, bool filt) : + QSortFilterProxyModel(parent), + filter{filt} { + return; } bool HopSortFilterProxyModel::lessThan(QModelIndex const & left, QModelIndex const & right) const { QVariant leftHop = sourceModel()->data(left); QVariant rightHop = sourceModel()->data(right); + // .:TODO:. Change this to use Hop::useDisplayNames QStringList uses = QStringList() << "Dry Hop" << "Aroma" << "Boil" << "First Wort" << "Mash"; - switch (left.column()) { - case HOPALPHACOL: + auto const columnIndex = static_cast(left.column()); + switch (columnIndex) { + case HopTableModel::ColumnIndex::Alpha: { double lAlpha = Localization::toDouble(leftHop.toString(), Q_FUNC_INFO); double rAlpha = Localization::toDouble(rightHop.toString(), Q_FUNC_INFO); return lAlpha < rAlpha; } - case HOPINVENTORYCOL: + case HopTableModel::ColumnIndex::Inventory: if (Measurement::qStringToSI(leftHop.toString(), Measurement::PhysicalQuantity::Mass).quantity() == 0.0 && this->sortOrder() == Qt::AscendingOrder) { return false; @@ -56,35 +58,40 @@ bool HopSortFilterProxyModel::lessThan(QModelIndex const & left, return Measurement::qStringToSI(leftHop.toString(), Measurement::PhysicalQuantity::Mass) < Measurement::qStringToSI(rightHop.toString(), Measurement::PhysicalQuantity::Mass); - case HOPAMOUNTCOL: + case HopTableModel::ColumnIndex::Amount: return Measurement::qStringToSI(leftHop.toString(), Measurement::PhysicalQuantity::Mass) < Measurement::qStringToSI(rightHop.toString(), Measurement::PhysicalQuantity::Mass); - case HOPTIMECOL: + case HopTableModel::ColumnIndex::Time: { - // Get the indexes of the Use column - QModelIndex lSibling = left.sibling(left.row(), HOPUSECOL); - QModelIndex rSibling = right.sibling(right.row(), HOPUSECOL); - // We are talking to the model, so we get the strings associated with - // the names, not the Hop::Use enums. We need those translated into - // ints to make this work - int lUse = uses.indexOf( (sourceModel()->data(lSibling)).toString() ); - int rUse = uses.indexOf( (sourceModel()->data(rSibling)).toString() ); + // Get the indexes of the Use column + QModelIndex lSibling = left.sibling( left.row(), static_cast(HopTableModel::ColumnIndex::Use)); + QModelIndex rSibling = right.sibling(right.row(), static_cast(HopTableModel::ColumnIndex::Use)); + // We are talking to the model, so we get the strings associated with + // the names, not the Hop::Use enums. We need those translated into + // ints to make this work + int lUse = uses.indexOf( (sourceModel()->data(lSibling)).toString() ); + int rUse = uses.indexOf( (sourceModel()->data(rSibling)).toString() ); - if (lUse == rUse) { - return Measurement::qStringToSI(leftHop.toString(), Measurement::PhysicalQuantity::Time) < - Measurement::qStringToSI(rightHop.toString(), Measurement::PhysicalQuantity::Time); - } + if (lUse == rUse) { + return Measurement::qStringToSI(leftHop.toString(), Measurement::PhysicalQuantity::Time) < + Measurement::qStringToSI(rightHop.toString(), Measurement::PhysicalQuantity::Time); + } - return lUse < rUse; + return lUse < rUse; } + + case HopTableModel::ColumnIndex::Name: + case HopTableModel::ColumnIndex::Form: + case HopTableModel::ColumnIndex::Use : + // Nothing to do for these cases + break; } return leftHop.toString() < rightHop.toString(); } -bool HopSortFilterProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent) const -{ +bool HopSortFilterProxyModel::filterAcceptsRow( int source_row, const QModelIndex &source_parent) const { HopTableModel* model = qobject_cast(sourceModel()); QModelIndex index = sourceModel()->index(source_row, 0, source_parent); diff --git a/src/HopSortFilterProxyModel.h b/src/HopSortFilterProxyModel.h index 7e613d07..1c50272d 100644 --- a/src/HopSortFilterProxyModel.h +++ b/src/HopSortFilterProxyModel.h @@ -1,5 +1,6 @@ /*====================================================================================================================== - * HopSortFilterProxyModel.h is part of Brewken, and is copyright the following authors 2009-2014: + * HopSortFilterProxyModel.h is part of Brewken, and is copyright the following authors 2009-2023: + * • Matt Young * • Mik Firestone * • Philip Greggory Lee * @@ -25,16 +26,15 @@ * * \brief Proxy model for sorting hops. */ -class HopSortFilterProxyModel : public QSortFilterProxyModel -{ +class HopSortFilterProxyModel : public QSortFilterProxyModel { Q_OBJECT public: - HopSortFilterProxyModel(QObject *parent = 0, bool filt = true); + HopSortFilterProxyModel(QObject * parent = 0, bool filt = true); protected: - bool lessThan(const QModelIndex &left, const QModelIndex &right) const; - bool filterAcceptsRow( int source_row, const QModelIndex &source_parent) const; + bool lessThan(QModelIndex const & left, QModelIndex const & right) const; + bool filterAcceptsRow(int source_row, QModelIndex const & source_parent) const; private: bool filter; diff --git a/src/HydrometerTool.cpp b/src/HydrometerTool.cpp index e87c1252..462790c4 100644 --- a/src/HydrometerTool.cpp +++ b/src/HydrometerTool.cpp @@ -35,56 +35,63 @@ HydrometerTool::HydrometerTool(QWidget* parent) : QDialog(parent) { this->doLayout(); - connect(pushButton_convert, &QAbstractButton::clicked, this, &HydrometerTool::convert ); - connect(label_inputTemp, &BtLabel::changedSystemOfMeasurementOrScale, lineEdit_inputTemp, &BtAmountEdit::lineChanged); - connect(label_inputSg, &BtLabel::changedSystemOfMeasurementOrScale, lineEdit_inputSg, &BtAmountEdit::lineChanged); - connect(label_outputSg, &BtLabel::changedSystemOfMeasurementOrScale, lineEdit_outputSg, &BtAmountEdit::lineChanged); - connect(label_calibratedTemp, &BtLabel::changedSystemOfMeasurementOrScale, lineEdit_calibratedTemp, &BtAmountEdit::lineChanged); + SMART_FIELD_INIT_FS(HydrometerTool, label_inputSg , lineEdit_inputSg , double, Measurement::PhysicalQuantity::Density ); + SMART_FIELD_INIT_FS(HydrometerTool, label_outputSg , lineEdit_outputSg , double, Measurement::PhysicalQuantity::Density ); + SMART_FIELD_INIT_FS(HydrometerTool, label_calibratedTemp, lineEdit_calibratedTemp, double, Measurement::PhysicalQuantity::Temperature, 1); + SMART_FIELD_INIT_FS(HydrometerTool, label_inputTemp , lineEdit_inputTemp , double, Measurement::PhysicalQuantity::Temperature); + + this->lineEdit_calibratedTemp->setAmount(15.55555556); +/// lineEdit_outputSg->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); + + connect(this->pushButton_convert, &QAbstractButton::clicked, this, &HydrometerTool::convert ); + connect(this->label_inputTemp, &SmartLabel::changedSystemOfMeasurementOrScale, this->lineEdit_inputTemp, &SmartLineEdit::lineChanged); + connect(this->label_inputSg, &SmartLabel::changedSystemOfMeasurementOrScale, this->lineEdit_inputSg, &SmartLineEdit::lineChanged); + connect(this->label_outputSg, &SmartLabel::changedSystemOfMeasurementOrScale, this->lineEdit_outputSg, &SmartLineEdit::lineChanged); + connect(this->label_calibratedTemp, &SmartLabel::changedSystemOfMeasurementOrScale, this->lineEdit_calibratedTemp, &SmartLineEdit::lineChanged); QMetaObject::connectSlotsByName(this); return; } void HydrometerTool::doLayout() { + // .:TBD:. We should either drop calls to setObjectName below or move thm into the init functions + resize(279, 96); QHBoxLayout* hLayout = new QHBoxLayout(this); QFormLayout* formLayout = new QFormLayout(); groupBox_inputSg = new QGroupBox(this); - groupBox_inputSg->setProperty("configSection", QVariant(QStringLiteral("hydrometerTool"))); - label_inputSg = new BtDensityLabel(groupBox_inputSg); + label_inputSg = new SmartLabel(groupBox_inputSg); label_inputSg ->setContextMenuPolicy(Qt::CustomContextMenu); - lineEdit_inputSg = new BtDensityEdit(groupBox_inputSg); + lineEdit_inputSg = new SmartLineEdit(groupBox_inputSg); lineEdit_inputSg->setMinimumSize(QSize(80, 0)); lineEdit_inputSg->setMaximumSize(QSize(80, 16777215)); - label_inputTemp = new BtTemperatureLabel(groupBox_inputSg); + label_inputTemp = new SmartLabel(groupBox_inputSg); label_inputTemp ->setObjectName(QStringLiteral("label_inputTemp")); label_inputTemp ->setContextMenuPolicy(Qt::CustomContextMenu); - lineEdit_inputTemp = new BtTemperatureEdit(groupBox_inputSg); + lineEdit_inputTemp = new SmartLineEdit(groupBox_inputSg); lineEdit_inputTemp->setMinimumSize(QSize(80, 0)); lineEdit_inputTemp->setMaximumSize(QSize(80, 16777215)); lineEdit_inputTemp->setObjectName(QStringLiteral("lineEdit_inputTemp")); - label_calibratedTemp = new BtTemperatureLabel(groupBox_inputSg); + label_calibratedTemp = new SmartLabel(groupBox_inputSg); label_calibratedTemp ->setObjectName(QStringLiteral("label_calibratedTemp")); label_calibratedTemp ->setContextMenuPolicy(Qt::CustomContextMenu); - lineEdit_calibratedTemp = new BtTemperatureEdit(groupBox_inputSg); + lineEdit_calibratedTemp = new SmartLineEdit(groupBox_inputSg); lineEdit_calibratedTemp->setMinimumSize(QSize(80, 0)); lineEdit_calibratedTemp->setMaximumSize(QSize(80, 16777215)); lineEdit_calibratedTemp->setObjectName(QStringLiteral("lineEdit_calibratedTemp")); - lineEdit_calibratedTemp->setText(15.55555556,1); - label_outputSg = new BtDensityLabel(groupBox_inputSg); + label_outputSg = new SmartLabel(groupBox_inputSg); label_outputSg ->setContextMenuPolicy(Qt::CustomContextMenu); - lineEdit_outputSg = new BtDensityEdit(groupBox_inputSg); + lineEdit_outputSg = new SmartLineEdit(groupBox_inputSg); lineEdit_outputSg->setMinimumSize(QSize(80, 0)); lineEdit_outputSg->setMaximumSize(QSize(80, 16777215)); - lineEdit_outputSg->setForcedSystemOfMeasurement(Measurement::SystemOfMeasurement::SpecificGravity); lineEdit_outputSg->setReadOnly(true); @@ -92,7 +99,7 @@ void HydrometerTool::doLayout() { label_inputSg->setBuddy(lineEdit_inputSg); label_inputTemp->setBuddy(lineEdit_inputTemp); label_outputSg->setBuddy(lineEdit_outputSg); -#endif //QT_NO_SHORTCUT +#endif formLayout->setWidget(0, QFormLayout::LabelRole, label_inputSg); formLayout->setWidget(0, QFormLayout::FieldRole, lineEdit_inputSg); @@ -127,28 +134,27 @@ void HydrometerTool::doLayout() { void HydrometerTool::retranslateUi() { setWindowTitle(tr("Hydrometer Tool")); - label_inputSg->setText(tr("SG Reading")); - label_inputTemp->setText(tr("Temperature")); + label_inputSg ->setText(tr("SG Reading")); + label_inputTemp ->setText(tr("Temperature")); label_calibratedTemp->setText(tr("Hydrometer Calibration")); - label_outputSg->setText(tr("Adjust SG")); - - pushButton_convert->setText(tr("Convert")); + label_outputSg ->setText(tr("Adjust SG")); + pushButton_convert ->setText(tr("Convert")); #ifndef QT_NO_TOOLTIP - lineEdit_inputSg->setToolTip(tr("Measured gravity")); + lineEdit_inputSg ->setToolTip(tr("Measured gravity")); lineEdit_inputTemp->setToolTip(tr("Temperature")); - lineEdit_outputSg->setToolTip(tr("Corrected gravity")); + lineEdit_outputSg ->setToolTip(tr("Corrected gravity")); #endif return; } void HydrometerTool::convert() { double correctedGravity = Algorithms::correctSgForTemperature( - lineEdit_inputSg->toCanonical().quantity(), // measured gravity - lineEdit_inputTemp->toCanonical().quantity(), // temperature at time of reading in Celsius + lineEdit_inputSg ->toCanonical().quantity(), // measured gravity + lineEdit_inputTemp ->toCanonical().quantity(), // temperature at time of reading in Celsius lineEdit_calibratedTemp->toCanonical().quantity() // calibration temperature of hydrometer in Celsius ); - lineEdit_outputSg->setText(correctedGravity); + lineEdit_outputSg->setAmount(correctedGravity); return; } diff --git a/src/HydrometerTool.h b/src/HydrometerTool.h index 391e45cd..83866dd3 100644 --- a/src/HydrometerTool.h +++ b/src/HydrometerTool.h @@ -21,9 +21,8 @@ #include -#include "BtAmountEdit.h" -#include "BtLabel.h" -#include "BtLineEdit.h" +#include "widgets/SmartLabel.h" +#include "widgets/SmartLineEdit.h" class QEvent; class QGroupBox; @@ -37,17 +36,17 @@ class HydrometerTool : public QDialog { //! \name Public UI Variables //! @{ - QPushButton * pushButton_convert; - BtDensityLabel * label_inputSg; - BtDensityEdit * lineEdit_inputSg; - BtDensityLabel * label_outputSg; - BtDensityEdit * lineEdit_outputSg; - - BtTemperatureLabel * label_inputTemp; - BtTemperatureEdit * lineEdit_inputTemp; - BtTemperatureLabel * label_calibratedTemp; - BtTemperatureEdit * lineEdit_calibratedTemp; - QGroupBox * groupBox_inputSg; + QPushButton * pushButton_convert; + SmartLabel * label_inputSg; + SmartLineEdit * lineEdit_inputSg; + SmartLabel * label_outputSg; + SmartLineEdit * lineEdit_outputSg; + + SmartLabel * label_inputTemp; + SmartLineEdit * lineEdit_inputTemp; + SmartLabel * label_calibratedTemp; + SmartLineEdit * lineEdit_calibratedTemp; + QGroupBox * groupBox_inputSg; //! @} public slots: diff --git a/src/InventoryFormatter.cpp b/src/InventoryFormatter.cpp index 5486d97f..ddf1fb12 100644 --- a/src/InventoryFormatter.cpp +++ b/src/InventoryFormatter.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * InventoryFormatter.cpp is part of Brewken, and is copyright the following authors 2016-2022: + * InventoryFormatter.cpp is part of Brewken, and is copyright the following authors 2016-2023: * • Mark de Wever * • Mattias Måhl * • Matt Young @@ -72,9 +72,8 @@ namespace { "%2" "") .arg(fermentable->name()) - .arg(Measurement::displayAmount(Measurement::Amount{fermentable->inventory(), Measurement::Units::kilograms}, - PersistentSettings::Sections::fermentableTable, - PropertyNames::NamedEntityWithInventory::inventory)); + .arg(Measurement::displayAmount(Measurement::Amount{fermentable->inventory(), + Measurement::Units::kilograms})); } result += ""; } @@ -111,9 +110,8 @@ namespace { "") .arg(hop->name()) .arg(hop->alpha_pct()) - .arg(Measurement::displayAmount(Measurement::Amount{hop->inventory(), Measurement::Units::kilograms}, - PersistentSettings::Sections::hopTable, - PropertyNames::NamedEntityWithInventory::inventory)); + .arg(Measurement::displayAmount(Measurement::Amount{hop->inventory(), + Measurement::Units::kilograms})); } result += ""; } @@ -145,9 +143,7 @@ namespace { Measurement::Amount{ miscellaneous->inventory(), miscellaneous->amountIsWeight() ? Measurement::Units::kilograms : Measurement::Units::liters - }, - PersistentSettings::Sections::miscTable, - PropertyNames::NamedEntityWithInventory::inventory + } ); result += QString("" "%1" @@ -184,9 +180,7 @@ namespace { Measurement::Amount{ yeast->inventory(), yeast->amountIsWeight() ? Measurement::Units::kilograms : Measurement::Units::liters - }, - PersistentSettings::Sections::yeastTable, - PropertyNames::NamedEntityWithInventory::inventory + } ); result += QString("" diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 7082cf23..aac45faa 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -81,29 +81,34 @@ #include "ConverterTool.h" #include "database/Database.h" #include "database/ObjectStoreWrapper.h" -#include "EquipmentEditor.h" +#include "editors/EquipmentEditor.h" +#include "editors/FermentableEditor.h" +#include "editors/HopEditor.h" +#include "editors/MashEditor.h" +#include "editors/MashStepEditor.h" +#include "editors/MiscEditor.h" +#include "editors/NamedMashEditor.h" +#include "editors/StyleEditor.h" +#include "editors/WaterEditor.h" +#include "editors/YeastEditor.h" #include "EquipmentListModel.h" -#include "FermentableDialog.h" -#include "FermentableEditor.h" #include "FermentableSortFilterProxyModel.h" #include "HelpDialog.h" -#include "HopDialog.h" -#include "HopEditor.h" #include "HopSortFilterProxyModel.h" #include "Html.h" #include "HydrometerTool.h" #include "ImportExport.h" +#include "ingredientDialogs/FermentableDialog.h" +#include "ingredientDialogs/HopDialog.h" +#include "ingredientDialogs/MiscDialog.h" +#include "ingredientDialogs/YeastDialog.h" #include "InventoryFormatter.h" #include "MashDesigner.h" -#include "MashEditor.h" #include "MashListModel.h" -#include "MashStepEditor.h" #include "MashWizard.h" +#include "measurement/Measurement.h" #include "measurement/Unit.h" -#include "MiscDialog.h" -#include "MiscEditor.h" #include "MiscSortFilterProxyModel.h" -#include "measurement/Measurement.h" #include "model/BrewNote.h" #include "model/Equipment.h" #include "model/Fermentable.h" @@ -111,7 +116,6 @@ #include "model/Recipe.h" #include "model/Style.h" #include "model/Yeast.h" -#include "NamedMashEditor.h" #include "OgAdjuster.h" #include "OptionDialog.h" #include "PersistentSettings.h" @@ -121,10 +125,8 @@ #include "RangedSlider.h" #include "RecipeFormatter.h" #include "RefractoDialog.h" -#include "RelationalUndoableUpdate.h" #include "ScaleRecipeTool.h" #include "StrikeWaterDialog.h" -#include "StyleEditor.h" #include "StyleListModel.h" #include "StyleSortFilterProxyModel.h" #include "tableModels/FermentableTableModel.h" @@ -133,15 +135,13 @@ #include "tableModels/MiscTableModel.h" #include "tableModels/YeastTableModel.h" #include "TimerMainDialog.h" -#include "UndoableAddOrRemove.h" -#include "UndoableAddOrRemoveList.h" +#include "undoRedo/RelationalUndoableUpdate.h" +#include "undoRedo/UndoableAddOrRemove.h" +#include "undoRedo/UndoableAddOrRemoveList.h" #include "utils/BtStringConst.h" #include "utils/OptionalHelpers.h" #include "WaterDialog.h" -#include "WaterEditor.h" #include "WaterListModel.h" -#include "YeastDialog.h" -#include "YeastEditor.h" #include "YeastSortFilterProxyModel.h" namespace { @@ -176,6 +176,44 @@ namespace { mainWindowInstance = new MainWindow(); return; } + + /** + * + */ + void updateDensitySlider(RangedSlider & slider, + SmartLabel const & label, + double const minCanonicalValue, + double const maxCanonicalValue, + double const maxPossibleCanonicalValue) { + slider.setPreferredRange(label.getRangeToDisplay(minCanonicalValue, maxCanonicalValue )); + slider.setRange (label.getRangeToDisplay(1.000 , maxPossibleCanonicalValue)); + + Measurement::UnitSystem const & displayUnitSystem = label.getDisplayUnitSystem(); + if (displayUnitSystem == Measurement::UnitSystems::density_Plato) { + slider.setPrecision(1); + slider.setTickMarks(2, 5); + } else { + slider.setPrecision(3); + slider.setTickMarks(0.010, 2); + } + return; + } + + /** + * + */ + void updateColorSlider(RangedSlider & slider, + SmartLabel const & label, + double const minCanonicalValue, + double const maxCanonicalValue) { + slider.setPreferredRange(label.getRangeToDisplay(minCanonicalValue, maxCanonicalValue)); + slider.setRange (label.getRangeToDisplay(1 , 44 )); + + Measurement::UnitSystem const & displayUnitSystem = label.getDisplayUnitSystem(); + slider.setTickMarks(displayUnitSystem == Measurement::UnitSystems::color_StandardReferenceMethod ? 10 : 40, 2); + + return; + } } // This private implementation class holds all private non-virtual members of MainWindow @@ -190,10 +228,11 @@ class MainWindow::impl { ~impl() = default; - // TODO Try making this a smart pointer HelpDialog * helpDialog; + + private: MainWindow & self; QFileDialog* fileOpener; @@ -203,11 +242,27 @@ class MainWindow::impl { MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), pimpl{std::make_unique(*this)} { qDebug() << Q_FUNC_INFO; - undoStack = new QUndoStack(this); + this->undoStack = new QUndoStack(this); // Need to call this parent class method to get all the widgets added (I think). this->setupUi(this); + // Initialise smart labels etc early, but after call to this->setupUi() because otherwise member variables such as + // label_name will not yet be set. + // .:TBD:. We should fix some of these inconsistently-named labels + SMART_FIELD_INIT(MainWindow, label_name , lineEdit_name , Recipe, PropertyNames::NamedEntity::name ); + SMART_FIELD_INIT(MainWindow, label_targetBatchSize, lineEdit_batchSize , Recipe, PropertyNames::Recipe::batchSize_l , 2); + SMART_FIELD_INIT(MainWindow, label_targetBoilSize , lineEdit_boilSize , Recipe, PropertyNames::Recipe::boilSize_l , 2); + SMART_FIELD_INIT(MainWindow, label_efficiency , lineEdit_efficiency, Recipe, PropertyNames::Recipe::efficiency_pct, 1); + SMART_FIELD_INIT(MainWindow, label_boilTime , lineEdit_boilTime , Recipe, PropertyNames::Recipe::boilTime_min , 1); + SMART_FIELD_INIT(MainWindow, label_boilSg , lineEdit_boilSg , Recipe, PropertyNames::Recipe::boilGrav , 3); + + SMART_FIELD_INIT_NO_SF(MainWindow, oGLabel , Recipe, PropertyNames::Recipe::og ); + SMART_FIELD_INIT_NO_SF(MainWindow, fGLabel , Recipe, PropertyNames::Recipe::fg ); + SMART_FIELD_INIT_NO_SF(MainWindow, colorSRMLabel , Recipe, PropertyNames::Recipe::color_srm ); + SMART_FIELD_INIT_NO_SF(MainWindow, label_batchSize, Recipe, PropertyNames::Recipe::boilSize_l); + SMART_FIELD_INIT_NO_SF(MainWindow, label_boilSize , Recipe, PropertyNames::Recipe::boilSize_l); + // Stop things looking ridiculously tiny on high DPI displays this->setSizesInPixelsBasedOnDpi(); @@ -240,11 +295,14 @@ MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), pimpl{std::make_u #else printer->setPageSize(QPageSize(QPageSize::Letter)); #endif + return; } void MainWindow::init() { qDebug() << Q_FUNC_INFO; + + this->setupCSS(); // initialize all of the dialog windows this->setupDialogs(); @@ -261,11 +319,6 @@ void MainWindow::init() { // do all the work for checkboxes (just one right now) this->setUpStateChanges(); - // This sets up things that might have been 'remembered' (ie stored in the config file) from a previous run of the - // program - eg window size, which is stored in MainWindow::closeEvent(). - // Breaks the naming convention, doesn't it? - this->restoreSavedState(); - // Connect menu item slots to triggered() signals this->setupTriggers(); // Connect pushbutton slots to clicked() signals @@ -279,6 +332,11 @@ void MainWindow::init() { // set up the drag/drop parts this->setupDrops(); + // This sets up things that might have been 'remembered' (ie stored in the config file) from a previous run of the + // program - eg window size, which is stored in MainWindow::closeEvent(). + // Breaks the naming convention, doesn't it? + this->restoreSavedState(); + // Moved from Database class Recipe::connectSignalsForAllRecipes(); qDebug() << Q_FUNC_INFO << "Recipe signals connected"; @@ -286,9 +344,9 @@ void MainWindow::init() { qDebug() << Q_FUNC_INFO << "Mash signals connected"; // I do not like this connection here. - connect(ancestorDialog, &AncestorDialog::ancestoryChanged, treeView_recipe->model(), &BtTreeModel::versionedRecipe); - connect(optionDialog, &OptionDialog::showAllAncestors, treeView_recipe->model(), &BtTreeModel::catchAncestors); - connect(treeView_recipe, &BtTreeView::recipeSpawn, this, &MainWindow::versionedRecipe); + connect(this->ancestorDialog, &AncestorDialog::ancestoryChanged, treeView_recipe->model(), &BtTreeModel::versionedRecipe); + connect(this->optionDialog, &OptionDialog::showAllAncestors, treeView_recipe->model(), &BtTreeModel::catchAncestors ); + connect(this->treeView_recipe, &BtTreeView::recipeSpawn, this, &MainWindow::versionedRecipe ); // No connections from the database yet? Oh FSM, that probably means I'm // doing it wrong again. @@ -430,52 +488,48 @@ void MainWindow::setupCSS() // Most dialogs are initialized in here. That should include any initial // configurations as well -void MainWindow::setupDialogs() -{ - dialog_about = new AboutDialog(this); - this->pimpl->helpDialog = new HelpDialog(this); - equipEditor = new EquipmentEditor(this); - singleEquipEditor = new EquipmentEditor(this, true); - fermDialog = new FermentableDialog(this); - fermEditor = new FermentableEditor(this); - hopDialog = new HopDialog(this); - hopEditor = new HopEditor(this); - mashEditor = new MashEditor(this); - mashStepEditor = new MashStepEditor(this); - mashWizard = new MashWizard(this); - miscDialog = new MiscDialog(this); - miscEditor = new MiscEditor(this); - styleEditor = new StyleEditor(this); - singleStyleEditor = new StyleEditor(this,true); - yeastDialog = new YeastDialog(this); - yeastEditor = new YeastEditor(this); - optionDialog = new OptionDialog(this); - recipeScaler = new ScaleRecipeTool(this); - recipeFormatter = new RecipeFormatter(this); - printAndPreviewDialog = new PrintAndPreviewDialog(this); - ogAdjuster = new OgAdjuster(this); - converterTool = new ConverterTool(this); - hydrometerTool = new HydrometerTool(this); - alcoholTool = new AlcoholTool(this); - timerMainDialog = new TimerMainDialog(this); - primingDialog = new PrimingDialog(this); - strikeWaterDialog = new StrikeWaterDialog(this); - refractoDialog = new RefractoDialog(this); - mashDesigner = new MashDesigner(this); - pitchDialog = new PitchDialog(this); - btDatePopup = new BtDatePopup(this); - - waterDialog = new WaterDialog(this); - waterEditor = new WaterEditor(this); - - ancestorDialog = new AncestorDialog(this); - +void MainWindow::setupDialogs() { + this->dialog_about = new AboutDialog(this); + this->pimpl->helpDialog = new HelpDialog(this); + this->equipEditor = new EquipmentEditor(this); + this->singleEquipEditor = new EquipmentEditor(this, true); + this->fermDialog = new FermentableDialog(this); + this->fermEditor = new FermentableEditor(this); + this->hopDialog = new HopDialog(this); + this->hopEditor = new HopEditor(this); + this->mashEditor = new MashEditor(this); + this->mashStepEditor = new MashStepEditor(this); + this->mashWizard = new MashWizard(this); + this->miscDialog = new MiscDialog(this); + this->miscEditor = new MiscEditor(this); + this->styleEditor = new StyleEditor(this); + this->singleStyleEditor = new StyleEditor(this,true); + this->yeastDialog = new YeastDialog(this); + this->yeastEditor = new YeastEditor(this); + this->optionDialog = new OptionDialog(this); + this->recipeScaler = new ScaleRecipeTool(this); + this->recipeFormatter = new RecipeFormatter(this); + this->printAndPreviewDialog = new PrintAndPreviewDialog(this); + this->ogAdjuster = new OgAdjuster(this); + this->converterTool = new ConverterTool(this); + this->hydrometerTool = new HydrometerTool(this); + this->alcoholTool = new AlcoholTool(this); + this->timerMainDialog = new TimerMainDialog(this); + this->primingDialog = new PrimingDialog(this); + this->strikeWaterDialog = new StrikeWaterDialog(this); + this->refractoDialog = new RefractoDialog(this); + this->mashDesigner = new MashDesigner(this); + this->pitchDialog = new PitchDialog(this); + this->btDatePopup = new BtDatePopup(this); + this->waterDialog = new WaterDialog(this); + this->waterEditor = new WaterEditor(this); + this->ancestorDialog = new AncestorDialog(this); + return; } // Configures the range widgets for the bubbles -void MainWindow::setupRanges() -{ +void MainWindow::setupRanges() { styleRangeWidget_og->setRange(1.000, 1.120); styleRangeWidget_og->setPrecision(3); styleRangeWidget_og->setTickMarks(0.010, 2); @@ -494,13 +548,13 @@ void MainWindow::setupRanges() // definitely cheating, but I don't feel like making a whole subclass just to support this // or the next. - rangeWidget_batchsize->setRange(0, recipeObs == nullptr ? 19.0 : recipeObs->batchSize_l()); - rangeWidget_batchsize->setPrecision(1); - rangeWidget_batchsize->setTickMarks(2,5); + rangeWidget_batchSize->setRange(0, recipeObs == nullptr ? 19.0 : recipeObs->batchSize_l()); + rangeWidget_batchSize->setPrecision(1); + rangeWidget_batchSize->setTickMarks(2,5); - rangeWidget_batchsize->setBackgroundBrush(QColor(255,255,255)); - rangeWidget_batchsize->setPreferredRangeBrush(QColor(55,138,251)); - rangeWidget_batchsize->setMarkerBrush(QBrush(Qt::NoBrush)); + rangeWidget_batchSize->setBackgroundBrush(QColor(255,255,255)); + rangeWidget_batchSize->setPreferredRangeBrush(QColor(55,138,251)); + rangeWidget_batchSize->setMarkerBrush(QBrush(Qt::NoBrush)); rangeWidget_boilsize->setRange(0, recipeObs == nullptr? 24.0 : recipeObs->boilVolume_l()); rangeWidget_boilsize->setPrecision(1); @@ -574,10 +628,10 @@ void MainWindow::setupTables() { // Set table models. // Fermentables - fermTableModel = new FermentableTableModel(fermentableTable); - fermTableProxy = new FermentableSortFilterProxyModel(fermentableTable,false); + fermTableModel = new FermentableTableModel(this->fermentableTable); + fermTableProxy = new FermentableSortFilterProxyModel(fermentableTable, false); fermTableProxy->setSourceModel(fermTableModel); - fermentableTable->setItemDelegate(new FermentableItemDelegate(fermentableTable)); + fermentableTable->setItemDelegate(new FermentableItemDelegate(fermentableTable, *fermTableModel)); fermentableTable->setModel(fermTableProxy); // Make the fermentable table show grain percentages in row headers. fermTableModel->setDisplayPercentages(true); @@ -591,7 +645,7 @@ void MainWindow::setupTables() hopTableModel = new HopTableModel(hopTable); hopTableProxy = new HopSortFilterProxyModel(hopTable, false); hopTableProxy->setSourceModel(hopTableModel); - hopTable->setItemDelegate(new HopItemDelegate(hopTable)); + hopTable->setItemDelegate(new HopItemDelegate(hopTable, *hopTableModel)); hopTable->setModel(hopTableProxy); // Hop table show IBUs in row headers. hopTableModel->setShowIBUs(true); @@ -604,7 +658,7 @@ void MainWindow::setupTables() miscTableModel = new MiscTableModel(miscTable); miscTableProxy = new MiscSortFilterProxyModel(miscTable,false); miscTableProxy->setSourceModel(miscTableModel); - miscTable->setItemDelegate(new MiscItemDelegate(miscTable)); + miscTable->setItemDelegate(new MiscItemDelegate(miscTable, *miscTableModel)); miscTable->setModel(miscTableProxy); connect( miscTable, &QTableView::doubleClicked, this, [&](const QModelIndex &idx) { if (idx.column() == 0) @@ -632,16 +686,16 @@ void MainWindow::setupTables() }); // Enable sorting in the main tables. - fermentableTable->horizontalHeader()->setSortIndicator( FERMAMOUNTCOL, Qt::DescendingOrder ); + fermentableTable->horizontalHeader()->setSortIndicator(static_cast(FermentableTableModel::ColumnIndex::Amount), Qt::DescendingOrder ); fermentableTable->setSortingEnabled(true); fermTableProxy->setDynamicSortFilter(true); - hopTable->horizontalHeader()->setSortIndicator( HOPTIMECOL, Qt::DescendingOrder ); + hopTable->horizontalHeader()->setSortIndicator(static_cast(HopTableModel::ColumnIndex::Time), Qt::DescendingOrder ); hopTable->setSortingEnabled(true); hopTableProxy->setDynamicSortFilter(true); - miscTable->horizontalHeader()->setSortIndicator( MISCUSECOL, Qt::DescendingOrder ); + miscTable->horizontalHeader()->setSortIndicator(static_cast(MiscTableModel::ColumnIndex::Use), Qt::DescendingOrder ); miscTable->setSortingEnabled(true); miscTableProxy->setDynamicSortFilter(true); - yeastTable->horizontalHeader()->setSortIndicator( YEASTNAMECOL, Qt::DescendingOrder ); + yeastTable->horizontalHeader()->setSortIndicator(static_cast(YeastTableModel::ColumnIndex::Name), Qt::DescendingOrder ); yeastTable->setSortingEnabled(true); yeastTableProxy->setDynamicSortFilter(true); } @@ -843,20 +897,20 @@ void MainWindow::setupActivate() { // lineEdits with either an editingFinished() or a textModified() should go in // here void MainWindow::setupTextEdit() { - connect(this->lineEdit_name, &QLineEdit::editingFinished, this, &MainWindow::updateRecipeName); - connect(this->lineEdit_batchSize, &BtLineEdit::textModified, this, &MainWindow::updateRecipeBatchSize); - connect(this->lineEdit_boilSize, &BtLineEdit::textModified, this, &MainWindow::updateRecipeBoilSize); - connect(this->lineEdit_boilTime, &BtLineEdit::textModified, this, &MainWindow::updateRecipeBoilTime); - connect(this->lineEdit_efficiency, &BtLineEdit::textModified, this, &MainWindow::updateRecipeEfficiency); + connect(this->lineEdit_name , &QLineEdit::editingFinished, this, &MainWindow::updateRecipeName); + connect(this->lineEdit_batchSize , &SmartLineEdit::textModified, this, &MainWindow::updateRecipeBatchSize); + connect(this->lineEdit_boilSize , &SmartLineEdit::textModified, this, &MainWindow::updateRecipeBoilSize); + connect(this->lineEdit_boilTime , &SmartLineEdit::textModified, this, &MainWindow::updateRecipeBoilTime); + connect(this->lineEdit_efficiency, &SmartLineEdit::textModified, this, &MainWindow::updateRecipeEfficiency); return; } -// anything using a BtLabel::changedSystemOfMeasurementOrScale signal should go in here +// anything using a SmartLabel::changedSystemOfMeasurementOrScale signal should go in here void MainWindow::setupLabels() { // These are the sliders. I need to consider these harder, but small steps - connect(this->oGLabel, &BtLabel::changedSystemOfMeasurementOrScale, this, &MainWindow::redisplayLabel); - connect(this->fGLabel, &BtLabel::changedSystemOfMeasurementOrScale, this, &MainWindow::redisplayLabel); - connect(this->colorSRMLabel, &BtLabel::changedSystemOfMeasurementOrScale, this, &MainWindow::redisplayLabel); + connect(this->oGLabel, &SmartLabel::changedSystemOfMeasurementOrScale, this, &MainWindow::redisplayLabel); + connect(this->fGLabel, &SmartLabel::changedSystemOfMeasurementOrScale, this, &MainWindow::redisplayLabel); + connect(this->colorSRMLabel, &SmartLabel::changedSystemOfMeasurementOrScale, this, &MainWindow::redisplayLabel); return; } @@ -958,25 +1012,25 @@ void MainWindow::treeActivated(const QModelIndex &index) { { Fermentable * ferm = active->getItem(index); if (ferm) { - fermEditor->setFermentable(ferm); + fermEditor->setEditItem(ObjectStoreWrapper::getSharedFromRaw(ferm)); fermEditor->show(); } } break; case BtTreeItem::Type::HOP: { - Hop* h = active->getItem(index); - if (h) { - hopEditor->setHop(h); + Hop* hop = active->getItem(index); + if (hop) { + hopEditor->setEditItem(ObjectStoreWrapper::getSharedFromRaw(hop)); hopEditor->show(); } } break; case BtTreeItem::Type::MISC: { - Misc * m = active->getItem(index); - if (m) { - miscEditor->setMisc(m); + Misc * misc = active->getItem(index); + if (misc) { + miscEditor->setEditItem(ObjectStoreWrapper::getSharedFromRaw(misc)); miscEditor->show(); } } @@ -992,9 +1046,9 @@ void MainWindow::treeActivated(const QModelIndex &index) { break; case BtTreeItem::Type::YEAST: { - Yeast * y = active->getItem(index); - if (y) { - yeastEditor->setYeast(y); + Yeast * yeast = active->getItem(index); + if (yeast) { + yeastEditor->setEditItem(ObjectStoreWrapper::getSharedFromRaw(yeast)); yeastEditor->show(); } } @@ -1094,8 +1148,7 @@ void MainWindow::setBrewNote(BrewNote* bNote) QString tabname; BrewNoteWidget* ni = findBrewNoteWidget(bNote); - if ( ni ) - { + if ( ni ) { tabWidget_recipeView->setCurrentWidget(ni); return; } @@ -1103,8 +1156,9 @@ void MainWindow::setBrewNote(BrewNote* bNote) ni = new BrewNoteWidget(tabWidget_recipeView); ni->setBrewNote(bNote); - tabWidget_recipeView->addTab(ni,bNote->brewDate_short()); - tabWidget_recipeView->setCurrentWidget(ni); + this->tabWidget_recipeView->addTab(ni,bNote->brewDate_short()); + this->tabWidget_recipeView->setCurrentWidget(ni); + return; } void MainWindow::setAncestor() @@ -1208,16 +1262,16 @@ void MainWindow::setRecipe(Recipe* recipe) { } // When a recipe is locked, many fields need to be disabled. -void MainWindow::lockRecipe(int state) -{ - if ( this->recipeObs == nullptr ) +void MainWindow::lockRecipe(int state) { + if (!this->recipeObs) { return; + } // If I am locking a recipe (lock == true ), I want to disable fields // (enable == false). If I am unlocking (lock == false), I want to enable // fields (enable == true). This just makes that easy - bool lockIt = state == Qt::Checked; - bool enabled = ! lockIt; + bool const lockIt = state == Qt::Checked; + bool const enabled = ! lockIt; // Lock/unlock the recipe, then disable/enable the fields. I am leaving the // name field as editable. I may regret that, but if you are defining an @@ -1262,13 +1316,13 @@ void MainWindow::lockRecipe(int state) pushButton_removeYeast->setEnabled(enabled); pushButton_editYeast->setEnabled(enabled); - fermDialog->pushButton_addToRecipe->setEnabled(enabled); - hopDialog->pushButton_addToRecipe->setEnabled(enabled); - miscDialog->pushButton_addToRecipe->setEnabled(enabled); - yeastDialog->pushButton_addToRecipe->setEnabled(enabled); + fermDialog ->setEnableAddToRecipe(enabled); + hopDialog ->setEnableAddToRecipe(enabled); + miscDialog ->setEnableAddToRecipe(enabled); + yeastDialog->setEnableAddToRecipe(enabled); // mashes still need dealing with // - + return; } void MainWindow::changed(QMetaProperty prop, QVariant val) { @@ -1287,60 +1341,6 @@ void MainWindow::changed(QMetaProperty prop, QVariant val) { return; } -void MainWindow::updateDensitySlider(BtStringConst const & propertyNameMin, - BtStringConst const & propertyNameMax, - BtStringConst const & propertyNameCurrent, - RangedSlider* slider, - double max) { - Measurement::UnitSystem const & displayUnitSystem = - Measurement::getUnitSystemForField(*propertyNameCurrent, - *PersistentSettings::Sections::tab_recipe, - Measurement::PhysicalQuantity::Density); - slider->setPreferredRange(Measurement::displayRange(recStyle, - this->tab_recipe, - propertyNameMin, - propertyNameMax, - &Measurement::Units::sp_grav)); - slider->setRange(Measurement::displayRange(this->tab_recipe, - propertyNameCurrent, - 1.000, - max, - Measurement::Units::sp_grav)); - - if (displayUnitSystem == Measurement::UnitSystems::density_Plato) { - slider->setPrecision(1); - slider->setTickMarks(2,5); - } else { - slider->setPrecision(3); - slider->setTickMarks(0.010, 2); - } - return; -} - -void MainWindow::updateColorSlider(BtStringConst const & propertyNameMin, - BtStringConst const & propertyNameMax, - BtStringConst const & propertyNameCurrent, - RangedSlider * slider) { - Measurement::UnitSystem const & displayUnitSystem = - Measurement::getUnitSystemForField(*propertyNameCurrent, - *PersistentSettings::Sections::tab_recipe, - Measurement::PhysicalQuantity::Color); - - slider->setPreferredRange(Measurement::displayRange(recStyle, - this->tab_recipe, - propertyNameMin, - propertyNameMax, - &Measurement::Units::srm)); - slider->setRange(Measurement::displayRange(this->tab_recipe, - propertyNameCurrent, - 1, - 44, - Measurement::Units::srm)); - slider->setTickMarks(displayUnitSystem == Measurement::UnitSystems::color_StandardReferenceMethod ? 10 : 40, 2); - - return; -} - void MainWindow::showChanges(QMetaProperty* prop) { if (recipeObs == nullptr) { return; @@ -1354,16 +1354,16 @@ void MainWindow::showChanges(QMetaProperty* prop) { } // May St. Stevens preserve me - lineEdit_name ->setText(recipeObs->name ()); - lineEdit_batchSize ->setText(recipeObs->batchSize_l ()); - lineEdit_boilSize ->setText(recipeObs->boilSize_l ()); - lineEdit_efficiency->setText(recipeObs->efficiency_pct()); - lineEdit_boilTime ->setText(recipeObs->boilTime_min ()); - lineEdit_name ->setCursorPosition(0); - lineEdit_batchSize ->setCursorPosition(0); - lineEdit_boilSize ->setCursorPosition(0); - lineEdit_efficiency->setCursorPosition(0); - lineEdit_boilTime ->setCursorPosition(0); + this->lineEdit_name ->setText (this->recipeObs->name ()); + this->lineEdit_batchSize ->setAmount(this->recipeObs->batchSize_l ()); + this->lineEdit_boilSize ->setAmount(this->recipeObs->boilSize_l ()); + this->lineEdit_efficiency->setAmount(this->recipeObs->efficiency_pct()); + this->lineEdit_boilTime ->setAmount(this->recipeObs->boilTime_min ()); + this->lineEdit_name ->setCursorPosition(0); + this->lineEdit_batchSize ->setCursorPosition(0); + this->lineEdit_boilSize ->setCursorPosition(0); + this->lineEdit_efficiency->setCursorPosition(0); + this->lineEdit_boilTime ->setCursorPosition(0); /* lineEdit_calcBatchSize->setText(recipeObs); lineEdit_calcBoilSize->setText(recipeObs); @@ -1385,31 +1385,42 @@ void MainWindow::showChanges(QMetaProperty* prop) { else lineEdit_calcBoilSize->setStyleSheet(highSS); */ - lineEdit_boilSg->setText(recipeObs); + this->lineEdit_boilSg->setAmount(this->recipeObs->boilGrav()); - updateDensitySlider(PropertyNames::Style::ogMin, PropertyNames::Style::ogMax, PropertyNames::Recipe::og, styleRangeWidget_og, 1.120); - styleRangeWidget_og->setValue(Measurement::amountDisplay(recipeObs, tab_recipe, PropertyNames::Recipe::og, &Measurement::Units::sp_grav)); + Style const * style = this->recipeObs->style(); + if (style) { + updateDensitySlider(*this->styleRangeWidget_og, *this->oGLabel, style->ogMin(), style->ogMax(), 1.120); + } + this->styleRangeWidget_og->setValue(this->oGLabel->getAmountToDisplay(recipeObs->og())); - updateDensitySlider(PropertyNames::Style::fgMin, PropertyNames::Style::fgMax, PropertyNames::Recipe::fg, styleRangeWidget_fg, 1.03); - styleRangeWidget_fg->setValue(Measurement::amountDisplay(recipeObs, tab_recipe, PropertyNames::Recipe::fg, &Measurement::Units::sp_grav)); + if (style) { + updateDensitySlider(*this->styleRangeWidget_fg, *this->fGLabel, style->fgMin(), style->fgMax(), 1.030); + } + this->styleRangeWidget_fg->setValue(this->fGLabel->getAmountToDisplay(recipeObs->fg())); - styleRangeWidget_abv->setValue(recipeObs->ABV_pct()); - styleRangeWidget_ibu->setValue(recipeObs->IBU()); + this->styleRangeWidget_abv->setValue(recipeObs->ABV_pct()); + this->styleRangeWidget_ibu->setValue(recipeObs->IBU()); - rangeWidget_batchsize->setRange(0, Measurement::amountDisplay(recipeObs, tab_recipe, PropertyNames::Recipe::batchSize_l, &Measurement::Units::liters)); - rangeWidget_batchsize->setPreferredRange(0, Measurement::amountDisplay(recipeObs, tab_recipe, PropertyNames::Recipe::finalVolume_l, &Measurement::Units::liters)); - rangeWidget_batchsize->setValue(Measurement::amountDisplay(recipeObs, tab_recipe, PropertyNames::Recipe::finalVolume_l, &Measurement::Units::liters)); + this->rangeWidget_batchSize->setRange (0, + this->label_batchSize->getAmountToDisplay(this->recipeObs->batchSize_l())); + this->rangeWidget_batchSize->setPreferredRange(0, + this->label_batchSize->getAmountToDisplay(this->recipeObs->finalVolume_l())); + this->rangeWidget_batchSize->setValue (this->label_batchSize->getAmountToDisplay(this->recipeObs->finalVolume_l())); - rangeWidget_boilsize->setRange(0, Measurement::amountDisplay(recipeObs, tab_recipe, PropertyNames::Recipe::boilSize_l, &Measurement::Units::liters)); - rangeWidget_boilsize->setPreferredRange(0, Measurement::amountDisplay(recipeObs, tab_recipe, PropertyNames::Recipe::boilVolume_l, &Measurement::Units::liters)); - rangeWidget_boilsize->setValue(Measurement::amountDisplay(recipeObs, tab_recipe, PropertyNames::Recipe::boilVolume_l, &Measurement::Units::liters)); + this->rangeWidget_boilsize->setRange (0, + this->label_boilSize->getAmountToDisplay(this->recipeObs->boilSize_l())); + this->rangeWidget_boilsize->setPreferredRange(0, + this->label_boilSize->getAmountToDisplay(this->recipeObs->boilVolume_l())); + this->rangeWidget_boilsize->setValue (this->label_boilSize->getAmountToDisplay(this->recipeObs->boilVolume_l())); /* Colors need the same basic treatment as gravity */ - updateColorSlider(PropertyNames::Style::colorMin_srm, - PropertyNames::Style::colorMax_srm, - PropertyNames::Recipe::color_srm, - styleRangeWidget_srm); - styleRangeWidget_srm->setValue(Measurement::amountDisplay(recipeObs, tab_recipe, PropertyNames::Recipe::color_srm, &Measurement::Units::srm)); + if (style) { + updateColorSlider(*this->styleRangeWidget_srm, + *this->colorSRMLabel, + style->colorMin_srm(), + style->colorMax_srm()); + } + this->styleRangeWidget_srm->setValue(this->colorSRMLabel->getAmountToDisplay(this->recipeObs->color_srm())); // In some, incomplete, recipes, OG is approximately 1.000, which then makes GU close to 0 and thus IBU/GU insanely // large. Besides being meaningless, such a large number takes up a lot of space. So, where gravity units are @@ -1444,7 +1455,10 @@ void MainWindow::updateRecipeName() { return; } - this->doOrRedoUpdate(*this->recipeObs, PropertyNames::NamedEntity::name, lineEdit_name->text(), tr("Change Recipe Name")); + this->doOrRedoUpdate(*this->recipeObs, + TYPE_INFO(Recipe, NamedEntity, name), + lineEdit_name->text(), + tr("Change Recipe Name")); return; } @@ -1458,25 +1472,14 @@ void MainWindow::displayRangesEtcForCurrentRecipeStyle() { return; } - styleRangeWidget_og->setPreferredRange(Measurement::displayRange(style, - this->tab_recipe, - PropertyNames::Style::ogMin, - PropertyNames::Style::ogMax, - &Measurement::Units::sp_grav)); - styleRangeWidget_fg->setPreferredRange(Measurement::displayRange(style, - this->tab_recipe, - PropertyNames::Style::fgMin, - PropertyNames::Style::fgMax, - &Measurement::Units::sp_grav)); + this->styleRangeWidget_og->setPreferredRange(this->oGLabel->getRangeToDisplay(style->ogMin(), style->ogMax())); - styleRangeWidget_abv->setPreferredRange(style->abvMin_pct(), style->abvMax_pct()); - styleRangeWidget_ibu->setPreferredRange(style->ibuMin(), style->ibuMax()); - styleRangeWidget_srm->setPreferredRange(Measurement::displayRange(style, - this->tab_recipe, - PropertyNames::Style::colorMin_srm, - PropertyNames::Style::colorMax_srm, - &Measurement::Units::srm)); + this->styleRangeWidget_fg->setPreferredRange(this->fGLabel->getRangeToDisplay(style->ogMin(), style->ogMax())); + this->styleRangeWidget_abv->setPreferredRange(style->abvMin_pct(), style->abvMax_pct()); + this->styleRangeWidget_ibu->setPreferredRange(style->ibuMin(), style->ibuMax()); + this->styleRangeWidget_srm->setPreferredRange(this->colorSRMLabel->getRangeToDisplay(style->colorMin_srm(), + style->colorMax_srm())); this->styleButton->setStyle(style); return; @@ -1551,8 +1554,8 @@ void MainWindow::droppedRecipeEquipment(Equipment *kit) { // Keep the mash tun weight and specific heat up to date. Mash * m = recipeObs->mash(); if (m) { - new SimpleUndoableUpdate(*m, PropertyNames::Mash::tunWeight_kg, kit->tunWeight_kg(), tr("Change Tun Weight"), equipmentUpdate); - new SimpleUndoableUpdate(*m, PropertyNames::Mash::tunSpecificHeat_calGC, kit->tunSpecificHeat_calGC(), tr("Change Tun Specific Heat"), equipmentUpdate); + new SimpleUndoableUpdate(*m, TYPE_INFO(Mash, tunWeight_kg ), kit->tunWeight_kg() , tr("Change Tun Weight") , equipmentUpdate); + new SimpleUndoableUpdate(*m, TYPE_INFO(Mash, tunSpecificHeat_calGC), kit->tunSpecificHeat_calGC(), tr("Change Tun Specific Heat"), equipmentUpdate); } if (QMessageBox::question(this, @@ -1566,9 +1569,9 @@ void MainWindow::droppedRecipeEquipment(Equipment *kit) { // won't ever be seen by the user, but there's no harm in setting them. // (The previous call here to mashEditor->setRecipe() was a roundabout way of calling setTunWeight_kg() and // setTunSpecificHeat_calGC() on the mash.) - new SimpleUndoableUpdate(*this->recipeObs, PropertyNames::Recipe::batchSize_l, kit->batchSize_l(), tr("Change Batch Size"), equipmentUpdate); - new SimpleUndoableUpdate(*this->recipeObs, PropertyNames::Recipe::boilSize_l, kit->boilSize_l(), tr("Change Boil Size"), equipmentUpdate); - new SimpleUndoableUpdate(*this->recipeObs, PropertyNames::Recipe::boilTime_min, kit->boilTime_min(), tr("Change Boil Time"), equipmentUpdate); + new SimpleUndoableUpdate(*this->recipeObs, TYPE_INFO(Recipe, batchSize_l ), kit->batchSize_l() , tr("Change Batch Size"), equipmentUpdate); + new SimpleUndoableUpdate(*this->recipeObs, TYPE_INFO(Recipe, boilSize_l ), kit->boilSize_l() , tr("Change Boil Size") , equipmentUpdate); + new SimpleUndoableUpdate(*this->recipeObs, TYPE_INFO(Recipe, boilTime_min), kit->boilTime_min(), tr("Change Boil Time") , equipmentUpdate); } // This will do the equipment update and any related updates - see above @@ -1674,7 +1677,7 @@ void MainWindow::updateRecipeBatchSize() { } this->doOrRedoUpdate(*this->recipeObs, - PropertyNames::Recipe::batchSize_l, + TYPE_INFO(Recipe, batchSize_l), lineEdit_batchSize->toCanonical().quantity(), tr("Change Batch Size")); } @@ -1685,7 +1688,7 @@ void MainWindow::updateRecipeBoilSize() { } this->doOrRedoUpdate(*this->recipeObs, - PropertyNames::Recipe::boilSize_l, + TYPE_INFO(Recipe, boilSize_l), lineEdit_boilSize->toCanonical().quantity(), tr("Change Boil Size")); } @@ -1702,28 +1705,28 @@ void MainWindow::updateRecipeBoilTime() { // recipeObs->boilSize_l // NOTE: This works because kit is the recipe's equipment, not the generic equipment in the recipe drop down. if (kit) { - this->doOrRedoUpdate(*kit, PropertyNames::Equipment::boilTime_min, boilTime, tr("Change Boil Time")); + this->doOrRedoUpdate(*kit, TYPE_INFO(Equipment, boilTime_min), boilTime, tr("Change Boil Time")); } else { - this->doOrRedoUpdate(*this->recipeObs, PropertyNames::Recipe::boilTime_min, boilTime, tr("Change Boil Time")); + this->doOrRedoUpdate(*this->recipeObs, TYPE_INFO(Recipe, boilTime_min), boilTime, tr("Change Boil Time")); } return; } void MainWindow::updateRecipeEfficiency() { - qDebug() << Q_FUNC_INFO << lineEdit_efficiency->getValueAs(); + qDebug() << Q_FUNC_INFO << lineEdit_efficiency->getNonOptValue(); if (!this->recipeObs) { return; } this->doOrRedoUpdate(*this->recipeObs, - PropertyNames::Recipe::efficiency_pct, - lineEdit_efficiency->getValueAs(), + TYPE_INFO(Recipe, efficiency_pct), + lineEdit_efficiency->getNonOptValue(), tr("Change Recipe Efficiency")); return; } -void MainWindow::addFermentableToRecipe(std::shared_ptr ferm) { +void MainWindow::addToRecipe(std::shared_ptr ferm) { Q_ASSERT(ferm); this->doOrRedoUpdate( newUndoableAddOrRemove(*this->recipeObs, @@ -1737,7 +1740,7 @@ void MainWindow::addFermentableToRecipe(std::shared_ptr ferm) { return; } -void MainWindow::addHopToRecipe(std::shared_ptr hop) { +void MainWindow::addToRecipe(std::shared_ptr hop) { Q_ASSERT(hop); this->doOrRedoUpdate( newUndoableAddOrRemove(*this->recipeObs, @@ -1750,7 +1753,7 @@ void MainWindow::addHopToRecipe(std::shared_ptr hop) { // triggered the necessary updates to hopTableModel. } -void MainWindow::addMiscToRecipe(std::shared_ptr misc) { +void MainWindow::addToRecipe(std::shared_ptr misc) { Q_ASSERT(misc); this->doOrRedoUpdate( newUndoableAddOrRemove(*this->recipeObs, @@ -1764,7 +1767,7 @@ void MainWindow::addMiscToRecipe(std::shared_ptr misc) { return; } -void MainWindow::addYeastToRecipe(std::shared_ptr yeast) { +void MainWindow::addToRecipe(std::shared_ptr yeast) { Q_ASSERT(yeast); this->doOrRedoUpdate( newUndoableAddOrRemove(*this->recipeObs, @@ -1837,20 +1840,17 @@ void MainWindow::doOrRedoUpdate(QUndoCommand * update) { return; } -void MainWindow::doOrRedoUpdate(QObject & updatee, - BtStringConst const & propertyName, +void MainWindow::doOrRedoUpdate(NamedEntity & updatee, + TypeInfo const & typeInfo, QVariant newValue, QString const & description, [[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)); + this->doOrRedoUpdate(new SimpleUndoableUpdate(updatee, typeInfo, newValue, description)); return; } // For undo/redo, we use Qt's Undo framework -void MainWindow::editUndo() -{ +void MainWindow::editUndo() { Q_ASSERT(this->undoStack != 0); if ( !this->undoStack->canUndo() ) { qDebug() << "Undo called but nothing to undo"; @@ -1862,8 +1862,7 @@ void MainWindow::editUndo() return; } -void MainWindow::editRedo() -{ +void MainWindow::editRedo() { Q_ASSERT(this->undoStack != 0); if ( !this->undoStack->canRedo() ) { qDebug() << "Redo called but nothing to redo"; @@ -2017,40 +2016,45 @@ void MainWindow::removeSelectedFermentable() { void MainWindow::editSelectedFermentable() { Fermentable* f = selectedFermentable(); - if( f == nullptr ) + if (!f) { return; + } - fermEditor->setFermentable(f); + fermEditor->setEditItem(ObjectStoreWrapper::getSharedFromRaw(f)); fermEditor->show(); + return; } void MainWindow::editSelectedMisc() { Misc* m = selectedMisc(); - if( m == nullptr ) + if (!m) { return; + } - miscEditor->setMisc(m); + miscEditor->setEditItem(ObjectStoreWrapper::getSharedFromRaw(m)); miscEditor->show(); } -void MainWindow::editSelectedHop() -{ +void MainWindow::editSelectedHop() { Hop* h = selectedHop(); - if( h == nullptr ) + if (!h) { return; + } - hopEditor->setHop(h); + hopEditor->setEditItem(ObjectStoreWrapper::getSharedFromRaw(h)); hopEditor->show(); + return; } -void MainWindow::editSelectedYeast() -{ +void MainWindow::editSelectedYeast() { Yeast* y = selectedYeast(); - if( y == nullptr ) + if (!y) { return; + } - yeastEditor->setYeast(y); + yeastEditor->setEditItem(ObjectStoreWrapper::getSharedFromRaw(y)); yeastEditor->show(); + return; } void MainWindow::removeSelectedHop() { @@ -2402,8 +2406,8 @@ void MainWindow::newBrewNote() { bIndex = treeView_recipe->findElement(bNote.get()); if ( bIndex.isValid() ) setTreeSelection(bIndex); + } } -} void MainWindow::reBrewNote() { QModelIndexList indexes = treeView_recipe->selectionModel()->selectedRows(); diff --git a/src/MainWindow.h b/src/MainWindow.h index 8e6d889e..94b63157 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -1,5 +1,5 @@ /*====================================================================================================================== - * MainWindow.h is part of Brewken, and is copyright the following authors 2009-2022: + * MainWindow.h is part of Brewken, and is copyright the following authors 2009-2023: * • Aidan Roberts * • Dan Cavanagh * • Daniel Pettersson @@ -44,7 +44,7 @@ #include #include "ui_mainWindow.h" -#include "SimpleUndoableUpdate.h" +#include "undoRedo/SimpleUndoableUpdate.h" // Forward Declarations @@ -140,6 +140,14 @@ class MainWindow : public QMainWindow, public Ui::mainWindow { void setBrewNoteByIndex(const QModelIndex &index); void setBrewNote(BrewNote* bNote); + //! \brief Doing updates via this method makes them undoable (and redoable). This is the simplified version + // which suffices for modifications to most individual non-relational attributes. + void doOrRedoUpdate(NamedEntity & updatee, + TypeInfo const & typeInfo, + QVariant newValue, + QString const & description, + QUndoCommand * parent = nullptr); + public slots: //! \brief Accepts Recipe changes, and takes appropriate action to show the changes. @@ -174,7 +182,7 @@ public slots: //! \brief Close a brewnote tab if we must (because of the BrewNote being deleted) void closeBrewNote(int brewNoteId, std::shared_ptr object); //! \brief Add given Fermentable to the Recipe. - void addFermentableToRecipe(std::shared_ptr ferm); + void addToRecipe(std::shared_ptr ferm); //! \brief Remove selected Fermentable(s) from the Recipe. void removeSelectedFermentable(); //! \brief Edit selected Fermentable. @@ -184,21 +192,21 @@ public slots: void showPitchDialog(); //! \brief Add given Hop to the Recipe. - void addHopToRecipe(std::shared_ptr hop); + void addToRecipe(std::shared_ptr hop); //! \brief Remove selected Hop(s) from the Recipe. void removeSelectedHop(); //! \brief Edit selected Hop. void editSelectedHop(); //! \brief Add given Misc to the Recipe. - void addMiscToRecipe(std::shared_ptr misc); + void addToRecipe(std::shared_ptr misc); //! \brief Remove selected Misc(s) from the Recipe. void removeSelectedMisc(); //! \brief Edit selected Misc. void editSelectedMisc(); //! \brief Add given Yeast to the Recipe. - void addYeastToRecipe(std::shared_ptr yeast); + void addToRecipe(std::shared_ptr yeast); //! \brief Remove selected Yeast(s) from the Recipe. void removeSelectedYeast(); //! \brief Edit selected Yeast @@ -302,15 +310,6 @@ public slots: */ void showChanges(QMetaProperty* prop = nullptr); -public: - //! \brief Doing updates via this method makes them undoable (and redoable). This is the simplified version - // which suffices for modifications to most individual non-relational attributes. - void doOrRedoUpdate(QObject & updatee, - BtStringConst const & propertyName, - QVariant newValue, - QString const & description, - QUndoCommand * parent = nullptr); - protected: virtual void closeEvent(QCloseEvent* event); @@ -463,16 +462,6 @@ private slots: //! \brief Connect signal/slots for check boxes void setUpStateChanges(); - void updateDensitySlider(BtStringConst const & propertyNameMin, - BtStringConst const & propertyNameMax, - BtStringConst const & propertyNameCurrent, - RangedSlider* slider, - double max); - void updateColorSlider(BtStringConst const & propertyNameMin, - BtStringConst const & propertyNameMax, - BtStringConst const & propertyNameCurrent, - RangedSlider* slider); - }; #endif diff --git a/src/MashComboBox.cpp b/src/MashComboBox.cpp deleted file mode 100644 index e8ff38a8..00000000 --- a/src/MashComboBox.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/*====================================================================================================================== - * MashComboBox.cpp is part of Brewken, and is copyright the following authors 2009-2022: - * • Jeff Bailey - * • Julein - * • Matt Young - * • Mik Firestone - * • Philip Greggory Lee - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "MashComboBox.h" - -#include - -#include "database/ObjectStoreWrapper.h" -#include "model/Mash.h" - -MashComboBox::MashComboBox(QWidget * parent) : - QComboBox(parent) { - this->setCurrentIndex(-1); - - connect(&ObjectStoreTyped::getInstance(), &ObjectStoreTyped::signalObjectInserted, this, &MashComboBox::addMash); - connect(&ObjectStoreTyped::getInstance(), &ObjectStoreTyped::signalObjectDeleted, this, &MashComboBox::removeMash); - this->repopulateList(); - return; -} - - -void MashComboBox::addMash(int mashId) { - this->add(ObjectStoreWrapper::getByIdRaw(mashId)); - return; -} - -void MashComboBox::add(Mash * m) { - if (m && !this->mashObs.contains(m) && m->display() && !m->deleted()) { - this->mashObs.append(m); - connect(m, SIGNAL(changed(QMetaProperty, QVariant)), this, SLOT(changed(QMetaProperty, QVariant))); - } - - this->addItem(m->name()); - return; -} - -void MashComboBox::removeMash([[maybe_unused]] int mashId, - std::shared_ptr object) { - this->remove(std::static_pointer_cast(object).get()); - return; -} - -void MashComboBox::remove(Mash * m) { - if (m) { - disconnect(m, 0, this, 0); - } - int ndx = this->mashObs.indexOf(m); - if (ndx >= 0) { - this->mashObs.removeAt(ndx); - this->removeItem(ndx); - } - return; -} - -void MashComboBox::removeAllMashs() { - QList tmpMashs(mashObs); - - for (int i = 0; i < tmpMashs.size(); ++i) { - this->remove(tmpMashs[i]); - } -} - -void MashComboBox::changed([[maybe_unused]] QMetaProperty prop, - [[maybe_unused]] QVariant val) { - int i = mashObs.indexOf(qobject_cast(sender())); - if (i >= 0) { - // Notice we assume 'i' is an index into both 'mashObs' and also - // to the text list in this combo box... - this->setItemText(i, mashObs[i]->name()); - } - return; -} - -void MashComboBox::setIndexByMash(Mash * mash) { - int ndx = mashObs.indexOf(mash); - this->setCurrentIndex(ndx); - return; -} - -void MashComboBox::setIndex(int ndx) { - this->setCurrentIndex(ndx); -} - -void MashComboBox::repopulateList() { - unsigned int i, size; - this->clear(); - - QList tmpMashs(mashObs); - size = tmpMashs.size(); - for (i = 0; i < size; ++i) { - this->remove(tmpMashs[i]); - } - - tmpMashs.clear(); - tmpMashs = ObjectStoreWrapper::getAllRaw(); - - size = tmpMashs.size(); - for (i = 0; i < size; ++i) { - this->add(tmpMashs[i]); - } - - this->setCurrentIndex(-1); - return; -} - -Mash * MashComboBox::getSelectedMash() { - if (this->currentIndex() >= 0) { - return this->mashObs[this->currentIndex()]; - } - return nullptr; -} diff --git a/src/MashComboBox.h b/src/MashComboBox.h deleted file mode 100644 index 600455f9..00000000 --- a/src/MashComboBox.h +++ /dev/null @@ -1,77 +0,0 @@ -/*====================================================================================================================== - * MashComboBox.h is part of Brewken, and is copyright the following authors 2009-2021: - * • Jeff Bailey - * • Matt Young - * • Philip Greggory Lee - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#ifndef MASHCOMBOBOX_H -#define MASHCOMBOBOX_H -#pragma once - -#include - -#include -#include -#include -#include -#include - -// Forward declaration. -class Mash; - -/*! - * \class MashComboBox - * - * \brief A combobox that is a view class for a list of mashes. - * - * Well, it's not exactly - * a strict view class, since it contains model-related methods, so we should - * prune out the model methods at some point. - */ -class MashComboBox : public QComboBox { - Q_OBJECT - -public: - MashComboBox(QWidget * parent = 0); - virtual ~MashComboBox() = default; - - //! Set the current index to that which corresponds to \b m. - void setIndexByMash(Mash * m); - //! Set the index. - void setIndex(int ndx); - //! Remove all mashs from the internal model. - void removeAllMashs(); - //! Populate the internal model with all the database mashs. - void repopulateList(); - - //! \return the selected mash. - Mash * getSelectedMash(); - - //! Add a mash to the internal model's list. - void add(Mash * m); - //! Remove a mash from the internal model's list. - void remove(Mash * m); - -public slots: - void changed(QMetaProperty, QVariant); - //! Add a mash to the list. - void addMash(int mashId); - //! Remove a mash from the list. - void removeMash(int mashId, std::shared_ptr object); - -private: - QList mashObs; -}; - -#endif diff --git a/src/MashDesigner.cpp b/src/MashDesigner.cpp index 4134e08a..7e5475e2 100644 --- a/src/MashDesigner.cpp +++ b/src/MashDesigner.cpp @@ -41,34 +41,38 @@ MashDesigner::MashDesigner(QWidget * parent) : QDialog {parent}, addedWater_l{0} { this->setupUi(this); - this->label_zeroVol->setText(Measurement::displayAmount(Measurement::Amount{0, Measurement::Units::liters})); + // .:TODO:. Would be good to make the label & field naming a bit more consistent in the .ui file + SMART_FIELD_INIT_FS(MashDesigner, label_targetTemp, lineEdit_temp, double, Measurement::PhysicalQuantity::Temperature, 1); // Target temp. + SMART_FIELD_INIT_FS(MashDesigner, label_stepTime, lineEdit_time, double, Measurement::PhysicalQuantity::Time, 0); // Time + + this->label_zeroVol ->setText(Measurement::displayAmount(Measurement::Amount{0, Measurement::Units::liters})); this->label_zeroWort->setText(Measurement::displayAmount(Measurement::Amount{0, Measurement::Units::liters})); // Update temp slider when we move amount slider. connect(horizontalSlider_amount, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateTempSlider); // Update amount slider when we move temp slider. - connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateAmtSlider); + connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateAmtSlider); // Update tun fullness bar when either slider moves. connect(horizontalSlider_amount, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateFullness); - connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateFullness); + connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateFullness); // Update amount/temp text when sliders move. connect(horizontalSlider_amount, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateAmt); connect(horizontalSlider_amount, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateTemp); - connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateAmt); - connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateTemp); + connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateAmt); + connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateTemp); // Update collected wort when sliders move. connect(horizontalSlider_amount, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateCollectedWort); - connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateCollectedWort); + connect(horizontalSlider_temp, &QAbstractSlider::sliderMoved, this, &MashDesigner::updateCollectedWort); // Save the target temp whenever it's changed. - connect(lineEdit_temp, &BtLineEdit::textModified, this, &MashDesigner::saveTargetTemp); + connect(lineEdit_temp, &SmartLineEdit::textModified, this, &MashDesigner::saveTargetTemp); // Move to next step. - connect(pushButton_next, &QAbstractButton::clicked, this, &MashDesigner::proceed); + connect(pushButton_next, &QAbstractButton::clicked, this, &MashDesigner::proceed); // Do correct calcs when the mash step type is selected. connect(comboBox_type, static_cast(&QComboBox::activated), this, &MashDesigner::typeChanged); // I still dislike this part. But I also need to "fix" the form // connect(checkBox_batchSparge, SIGNAL(clicked()), this, SLOT(updateMaxAmt())); - connect(pushButton_finish, &QAbstractButton::clicked, this, &MashDesigner::saveAndClose); + connect(pushButton_finish, &QAbstractButton::clicked, this, &MashDesigner::saveAndClose); return; } @@ -587,7 +591,7 @@ void MashDesigner::saveTargetTemp() { double temp = this->bound_temp_c(this->stepTemp_c()); // be nice and reset the field so it displays in proper units - this->lineEdit_temp->setText(temp); + this->lineEdit_temp->setAmount(temp); if (this->mashStep) { this->mashStep->setStepTemp_c(temp); } diff --git a/src/MashEditor.cpp b/src/MashEditor.cpp deleted file mode 100644 index ea8070d8..00000000 --- a/src/MashEditor.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/*====================================================================================================================== - * MashEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Kregg Kemper - * • Matt Young - * • Mik Firestone - * • Philip Greggory Lee - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "MashEditor.h" - -#include -#include - -#include "database/ObjectStoreWrapper.h" -#include "measurement/Unit.h" -#include "model/Equipment.h" -#include "model/Mash.h" -#include "model/Recipe.h" - -MashEditor::MashEditor(QWidget* parent) : QDialog(parent), mashObs(nullptr) { - setupUi(this); - - connect(pushButton_fromEquipment, &QAbstractButton::clicked, this, &MashEditor::fromEquipment ); - connect(this, &QDialog::accepted, this, &MashEditor::saveAndClose ); - connect(this, &QDialog::rejected, this, &MashEditor::closeEditor ); - return; -} - -void MashEditor::showEditor() { - showChanges(); - setVisible(true); - return; -} - -void MashEditor::closeEditor() { - setVisible(false); - return; -} - -void MashEditor::saveAndClose() { - bool isNew = false; - - if (this->mashObs == nullptr) { - this->mashObs = new Mash(lineEdit_name->text()); - isNew = true; - } - qDebug() << Q_FUNC_INFO << "Saving" << (isNew ? "new" : "existing") << "mash (#" << this->mashObs->key() << ")"; - - mashObs->setEquipAdjust(true); // BeerXML won't like me, but it's just stupid not to adjust for the equipment when you're able. - - mashObs->setName(lineEdit_name->text()); - mashObs->setGrainTemp_c(lineEdit_grainTemp->toCanonical().quantity()); - mashObs->setSpargeTemp_c(lineEdit_spargeTemp->toCanonical().quantity()); - mashObs->setPh(lineEdit_spargePh->toCanonical().quantity()); - mashObs->setTunTemp_c(lineEdit_tunTemp->toCanonical().quantity()); - mashObs->setTunWeight_kg(lineEdit_tunMass->toCanonical().quantity()); - mashObs->setTunSpecificHeat_calGC(lineEdit_tunSpHeat->toCanonical().quantity()); - - mashObs->setNotes(textEdit_notes->toPlainText()); - - if (isNew) { - ObjectStoreWrapper::insert(*mashObs); - this->m_rec->setMash(this->mashObs); - } - - return; -} - -void MashEditor::fromEquipment() { - if (this->mashObs == nullptr) { - return; - } - - if (this->m_equip == nullptr) { - return; - } - - lineEdit_tunMass->setText(m_equip); - lineEdit_tunSpHeat->setText(m_equip); - return; -} - -void MashEditor::setMash(Mash* mash) { - if (mashObs) { - disconnect( mashObs, nullptr, this, nullptr ); - } - - mashObs = mash; - if( mashObs ) - { - connect( mashObs, SIGNAL(changed(QMetaProperty,QVariant)), this, SLOT(changed(QMetaProperty,QVariant)) ); - showChanges(); - } - return; -} - -void MashEditor::setRecipe(Recipe* r) { - if ( ! r ) - return; - - this->m_rec = r; - this->m_equip = this->m_rec->equipment(); - - if (this->mashObs && this->m_equip) { - // Only do this if we have to. Otherwise, it causes some unnecessary updates to the database. - if (this->mashObs->tunWeight_kg() != this->m_equip->tunWeight_kg()) { - qDebug() << - Q_FUNC_INFO << "Overwriting mash tunWeight_kg (" << this->mashObs->tunWeight_kg() << ") with equipment " - "tunWeight_kg (" << this->m_equip->tunWeight_kg() << ")"; - this->mashObs->setTunWeight_kg(this->m_equip->tunWeight_kg()); - } - if (this->mashObs->tunSpecificHeat_calGC() != this->m_equip->tunSpecificHeat_calGC() ) { - qDebug() << - Q_FUNC_INFO << "Overwriting mash tunSpecificHeat_calGC (" << this->mashObs->tunSpecificHeat_calGC() << ") " - "with equipment tunSpecificHeat_calGC (" << this->m_equip->tunSpecificHeat_calGC() << ")"; - this->mashObs->setTunSpecificHeat_calGC(this->m_equip->tunSpecificHeat_calGC()); - } - } - return; -} - -void MashEditor::changed(QMetaProperty prop, QVariant /*val*/) { - if (sender() == this->mashObs ) { - this->showChanges(&prop); - } - - if (sender() == this->m_rec) { - this->m_equip = this->m_rec->equipment(); - this->showChanges(); - } - return; -} - -void MashEditor::showChanges(QMetaProperty* prop) { - bool updateAll = false; - QString propName; - - if( mashObs == nullptr ) - { - clear(); - return; - } - - if (prop == nullptr) { - updateAll = true; - } else { - propName = prop->name(); - } - qDebug() << Q_FUNC_INFO << "Updating" << (updateAll ? "all" : "property") << propName; - - if( propName == PropertyNames::NamedEntity::name || updateAll ) { - lineEdit_name->setText(mashObs->name()); - if( ! updateAll ) - return; - } - if( propName == PropertyNames::Mash::grainTemp_c || updateAll ) { - lineEdit_grainTemp->setText(mashObs); - if( ! updateAll ) - return; - } - if( propName == PropertyNames::Mash::spargeTemp_c || updateAll ) { - lineEdit_spargeTemp->setText(mashObs); - if( ! updateAll ) - return; - } - if( propName == PropertyNames::Mash::ph || updateAll ) { - lineEdit_spargePh->setText(mashObs); - if( ! updateAll ) - return; - } - if( propName == PropertyNames::Mash::tunTemp_c || updateAll ) { - lineEdit_tunTemp->setText(mashObs); - if( ! updateAll ) - return; - } - if( propName == PropertyNames::Mash::tunWeight_kg || updateAll ) { - lineEdit_tunMass->setText(mashObs); - if( ! updateAll ) - return; - } - if( propName == PropertyNames::Mash::tunSpecificHeat_calGC || updateAll ) { - lineEdit_tunSpHeat->setText(mashObs); - if( ! updateAll ) - return; - } - if( propName == PropertyNames::Mash::notes || updateAll ) { - textEdit_notes->setPlainText(mashObs->notes()); - if( ! updateAll ) - return; - } -} - -void MashEditor::clear() { - lineEdit_name->setText(QString("")); - lineEdit_grainTemp->setText(QString("")); - lineEdit_spargeTemp->setText(QString("")); - lineEdit_spargePh->setText(QString("")); - lineEdit_tunTemp->setText(QString("")); - lineEdit_tunMass->setText(QString("")); - lineEdit_tunSpHeat->setText(QString("")); - - textEdit_notes->setPlainText(QString("")); - return; -} diff --git a/src/MashStepEditor.cpp b/src/MashStepEditor.cpp deleted file mode 100644 index 23806b01..00000000 --- a/src/MashStepEditor.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/*====================================================================================================================== - * MashStepEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Matt Young - * • Mik Firestone - * • Philip Greggory Lee - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "MashStepEditor.h" - -#include "MainWindow.h" -#include "measurement/Unit.h" -#include "model/MashStep.h" - -MashStepEditor::MashStepEditor(QWidget* parent) : QDialog{parent}, obs(nullptr) { - this->setupUi(this); - - this->comboBox_type->setCurrentIndex(-1); - - connect(buttonBox, &QDialogButtonBox::accepted, this, &MashStepEditor::saveAndClose); - connect(buttonBox, &QDialogButtonBox::rejected, this, &MashStepEditor::close); - connect(this->comboBox_type, &QComboBox::currentTextChanged, this, &MashStepEditor::grayOutStuff); - return; -} - -void MashStepEditor::showChanges(QMetaProperty* metaProp) { - if (!this->obs) { - this->clear(); - return; - } - - QString propName; - QVariant value; - bool updateAll = false; - - if (metaProp == nullptr) { - updateAll = true; - } else { - propName = metaProp->name(); - value = metaProp->read(this->obs.get()); - } - - if (updateAll) { - lineEdit_name->setText(obs->name()); - comboBox_type->setCurrentIndex(static_cast(obs->type())); - lineEdit_infuseAmount->setText(this->obs.get()); - lineEdit_infuseTemp->setText(this->obs.get()); - lineEdit_decoctionAmount->setText(this->obs.get()); - lineEdit_stepTemp->setText(this->obs.get()); - lineEdit_stepTime->setText(this->obs.get()); - lineEdit_rampTime->setText(this->obs.get()); - lineEdit_endTemp->setText(this->obs.get()); - } else if (propName == PropertyNames::NamedEntity::name) { - lineEdit_name->setText(obs->name()); - } else if (propName == PropertyNames::MashStep::type) { - comboBox_type->setCurrentIndex(static_cast(obs->type())); - } else if (propName == PropertyNames::MashStep::infuseAmount_l) { - lineEdit_infuseAmount->setText(this->obs.get()); - } else if (propName == PropertyNames::MashStep::infuseTemp_c) { - lineEdit_infuseTemp->setText(this->obs.get()); - } else if (propName == PropertyNames::MashStep::decoctionAmount_l) { - lineEdit_decoctionAmount->setText(this->obs.get()); - } else if (propName == PropertyNames::MashStep::stepTemp_c) { - lineEdit_stepTemp->setText(this->obs.get()); - } else if (propName == PropertyNames::MashStep::stepTime_min) { - lineEdit_stepTime->setText(this->obs.get()); - } else if (propName == PropertyNames::MashStep::rampTime_min) { - lineEdit_rampTime->setText(this->obs.get()); - } else if (propName == PropertyNames::MashStep::endTemp_c) { - lineEdit_endTemp->setText(this->obs.get()); - } - return; -} - -void MashStepEditor::clear() { - lineEdit_name->setText(QString("")); - comboBox_type->setCurrentIndex(0); - lineEdit_infuseAmount->setText(QString("")); - lineEdit_infuseTemp->setText(QString("")); - lineEdit_decoctionAmount->setText(QString("")); - lineEdit_stepTemp->setText(QString("")); - lineEdit_stepTime->setText(QString("")); - lineEdit_rampTime->setText(QString("")); - lineEdit_endTemp->setText(QString("")); - return; -} - -void MashStepEditor::close() { - setVisible(false); - return; -} - -void MashStepEditor::changed(QMetaProperty prop, QVariant /*val*/) { - if (sender() != this->obs.get()) { - return; - } - - showChanges(&prop); - return; -} - -void MashStepEditor::setMashStep(std::shared_ptr step) { - if (this->obs) { - disconnect(this->obs.get(), nullptr, this, nullptr); - } - - this->obs = step; - - if (this->obs) { - connect(this->obs.get(), &MashStep::changed, this, &MashStepEditor::changed); - showChanges(); - } - return; -} - -void MashStepEditor::saveAndClose() { - obs->setName(lineEdit_name->text()); - obs->setType(static_cast(comboBox_type->currentIndex())); - obs->setInfuseAmount_l(lineEdit_infuseAmount->toCanonical().quantity()); - obs->setInfuseTemp_c(lineEdit_infuseTemp->toCanonical().quantity()); - obs->setDecoctionAmount_l(lineEdit_decoctionAmount->toCanonical().quantity()); - obs->setStepTemp_c(lineEdit_stepTemp->toCanonical().quantity()); - obs->setStepTime_min(lineEdit_stepTime->toCanonical().quantity()); - obs->setRampTime_min(lineEdit_rampTime->toCanonical().quantity()); - obs->setEndTemp_c(lineEdit_endTemp->toCanonical().quantity()); - - if (this->obs->key() < 0) { - // This is a new MashStep, so we need to store it. - // We'll ask MainWindow to do this for us, because then it can be an undoable action. - // - // The Mash of this MashStep should already have been set by the caller - MainWindow::instance().addMashStepToMash(this->obs); - } - - setVisible(false); - return; -} - -void MashStepEditor::grayOutStuff(const QString& text) { - if (text == "Infusion") { - lineEdit_infuseAmount->setEnabled(true); - lineEdit_infuseTemp->setEnabled(true); - lineEdit_decoctionAmount->setEnabled(false); - } else if (text == "Decoction") { - lineEdit_infuseAmount->setEnabled(false); - lineEdit_infuseTemp->setEnabled(false); - lineEdit_decoctionAmount->setEnabled(true); - } else if (text == "Temperature") { - lineEdit_infuseAmount->setEnabled(false); - lineEdit_infuseTemp->setEnabled(false); - lineEdit_decoctionAmount->setEnabled(false); - } else { - lineEdit_infuseAmount->setEnabled(true); - lineEdit_infuseTemp->setEnabled(true); - lineEdit_decoctionAmount->setEnabled(true); - } - return; -} diff --git a/src/MiscDialog.cpp b/src/MiscDialog.cpp deleted file mode 100644 index 25e4fdd9..00000000 --- a/src/MiscDialog.cpp +++ /dev/null @@ -1,231 +0,0 @@ -/*====================================================================================================================== - * MiscDialog.cpp is part of Brewken, and is copyright the following authors 2009-2022: - * • Brian Rower - * • Daniel Pettersson - * • Matt Young - * • Mik Firestone - * • Philip Greggory Lee - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "MiscDialog.h" - -#include -#include -#include -#include -#include - -//#include "database/Database.h" -#include "database/ObjectStoreWrapper.h" -#include "model/Recipe.h" -#include "MainWindow.h" -#include "model/Misc.h" -#include "MiscEditor.h" -#include "MiscSortFilterProxyModel.h" -#include "tableModels/MiscTableModel.h" - -MiscDialog::MiscDialog(MainWindow* parent) : - QDialog(parent), - mainWindow(parent), - numMiscs(0), - miscEdit(new MiscEditor(this)) -{ - doLayout(); - - miscTableModel = new MiscTableModel(tableWidget, false); - miscTableModel->setInventoryEditable(true); - miscTableProxy = new MiscSortFilterProxyModel(tableWidget); - miscTableProxy->setSourceModel(miscTableModel); - tableWidget->setModel(miscTableProxy); - tableWidget->setSortingEnabled(true); - tableWidget->sortByColumn( MISCNAMECOL, Qt::AscendingOrder ); - miscTableProxy->setDynamicSortFilter(true); - miscTableProxy->setFilterKeyColumn(1); - - connect( pushButton_addToRecipe, SIGNAL( clicked() ), this, SLOT( addMisc() ) ); - connect( pushButton_new, SIGNAL(clicked()), this, SLOT( newMisc() ) ); - connect( pushButton_edit, &QAbstractButton::clicked, this, &MiscDialog::editSelected ); - connect( pushButton_remove, &QAbstractButton::clicked, this, &MiscDialog::removeMisc ); - connect( tableWidget, &QAbstractItemView::doubleClicked, this, &MiscDialog::addMisc ); - connect( qLineEdit_searchBox, &QLineEdit::textEdited, this, &MiscDialog::filterMisc); - - miscTableModel->observeDatabase(true); -} - -void MiscDialog::doLayout() -{ - resize(800, 300); - verticalLayout = new QVBoxLayout(this); - tableWidget = new QTableView(this); - horizontalLayout = new QHBoxLayout(); - qLineEdit_searchBox = new QLineEdit(); - qLineEdit_searchBox->setMaxLength(30); - qLineEdit_searchBox->setPlaceholderText("Enter filter"); - horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - pushButton_addToRecipe = new QPushButton(this); - pushButton_addToRecipe->setObjectName(QStringLiteral("pushButton_addToRecipe")); - pushButton_addToRecipe->setAutoDefault(false); - pushButton_addToRecipe->setDefault(true); - pushButton_new = new QPushButton(this); - pushButton_new->setObjectName(QStringLiteral("pushButton_new")); - pushButton_new->setAutoDefault(false); - pushButton_edit = new QPushButton(this); - pushButton_edit->setObjectName(QStringLiteral("pushButton_edit")); - QIcon icon; - icon.addFile(QStringLiteral(":/images/edit.svg"), QSize(), QIcon::Normal, QIcon::Off); - pushButton_edit->setIcon(icon); - pushButton_edit->setAutoDefault(false); - pushButton_remove = new QPushButton(this); - pushButton_remove->setObjectName(QStringLiteral("pushButton_remove")); - QIcon icon1; - icon1.addFile(QStringLiteral(":/images/smallMinus.svg"), QSize(), QIcon::Normal, QIcon::Off); - pushButton_remove->setIcon(icon1); - pushButton_remove->setAutoDefault(false); - horizontalLayout->addWidget(qLineEdit_searchBox); - horizontalLayout->addItem(horizontalSpacer); - horizontalLayout->addWidget(pushButton_addToRecipe); - horizontalLayout->addWidget(pushButton_new); - horizontalLayout->addWidget(pushButton_edit); - horizontalLayout->addWidget(pushButton_remove); - verticalLayout->addWidget(tableWidget); - verticalLayout->addLayout(horizontalLayout); - - retranslateUi(); - QMetaObject::connectSlotsByName(this); -} - -void MiscDialog::retranslateUi() -{ - setWindowTitle(tr("Misc Database")); - pushButton_addToRecipe->setText(tr("Add to Recipe")); - pushButton_new->setText(tr("New")); - pushButton_edit->setText(QString()); - pushButton_remove->setText(QString()); -#ifndef QT_NO_TOOLTIP - pushButton_addToRecipe->setToolTip(tr("Add selected ingredient to recipe")); - pushButton_new->setToolTip(tr("Create new ingredient")); - pushButton_edit->setToolTip(tr("Edit selected ingredient")); - pushButton_remove->setToolTip(tr("Remove selected ingredient")); -#endif // QT_NO_TOOLTIP -} - -void MiscDialog::removeMisc() -{ - QModelIndexList selected = tableWidget->selectionModel()->selectedIndexes(); - int row, size, i; - - size = selected.size(); - if( size == 0 ) - return; - - // Make sure only one row is selected. - row = selected[0].row(); - for( i = 1; i < size; ++i ) - { - if( selected[i].row() != row ) - return; - } - - auto m = miscTableModel->getRow(miscTableProxy->mapToSource(selected[0]).row()); - ObjectStoreWrapper::softDelete(*m); - return; -} - -void MiscDialog::addMisc(const QModelIndex& index) -{ - QModelIndex translated; - - if( !index.isValid() ) - { - QModelIndexList selected = tableWidget->selectionModel()->selectedIndexes(); - int row, size, i; - - size = selected.size(); - if( size == 0 ) - return; - - // Make sure only one row is selected. - row = selected[0].row(); - for( i = 1; i < size; ++i ) - { - if( selected[i].row() != row ) - return; - } - - // Always need to translate indices through the proxy - translated = miscTableProxy->mapToSource(selected[0]); - } - else - { - // Only respond if the name is selected. Since we connect to double-click signal, - // this keeps us from adding something to the recipe when we just want to edit - // one of the other columns. - if( index.column() == MISCNAMECOL ) - translated = miscTableProxy->mapToSource(index); - else - return; - } - - MainWindow::instance().addMiscToRecipe(miscTableModel->getRow(translated.row())); - - return; -} - -void MiscDialog::editSelected() -{ - QModelIndexList selected = tableWidget->selectionModel()->selectedIndexes(); - int row, size, i; - - size = selected.size(); - if( size == 0 ) - return; - - // Make sure only one row is selected. - row = selected[0].row(); - for( i = 1; i < size; ++i ) - { - if( selected[i].row() != row ) - return; - } - - auto m = miscTableModel->getRow(miscTableProxy->mapToSource(selected[0]).row()); - miscEdit->setMisc(m.get()); - miscEdit->show(); - return; -} - -void MiscDialog::newMisc() -{ - newMisc(QString()); -} - -void MiscDialog::newMisc(QString folder) -{ - QString name = QInputDialog::getText(this, tr("Misc name"), - tr("Misc name:")); - if(name.isEmpty()) - return; - - Misc* m = new Misc(name); - if ( ! folder.isEmpty() ) - m->setFolder(folder); - - miscEdit->setMisc(m); - miscEdit->show(); -} - -void MiscDialog::filterMisc(QString searchExpression) -{ - miscTableProxy->setFilterCaseSensitivity(Qt::CaseInsensitive); - miscTableProxy->setFilterFixedString(searchExpression); -} diff --git a/src/MiscDialog.h b/src/MiscDialog.h deleted file mode 100644 index 4c16111d..00000000 --- a/src/MiscDialog.h +++ /dev/null @@ -1,96 +0,0 @@ -/*====================================================================================================================== - * MiscDialog.h is part of Brewken, and is copyright the following authors 2009-2015: - * • Daniel Pettersson - * • Jeff Bailey - * • Mik Firestone - * • Philip Greggory Lee - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#ifndef MISCDIALOG_H -#define MISCDIALOG_H -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -// Forward declarations. -class MainWindow; -class MiscEditor; -class MiscTableModel; -class MiscSortFilterProxyModel; - -/*! - * \class MiscDialog - * - * \brief View/controller dialog for the miscs in the database. - */ -class MiscDialog : public QDialog { - Q_OBJECT - -public: - MiscDialog(MainWindow* parent); - virtual ~MiscDialog() {} - - //! \name Public UI Variables - //! @{ - QVBoxLayout *verticalLayout; - QTableView *tableWidget; - QHBoxLayout *horizontalLayout; - QLineEdit *qLineEdit_searchBox; - QSpacerItem *horizontalSpacer; - QPushButton *pushButton_addToRecipe; - QPushButton *pushButton_new; - QPushButton *pushButton_edit; - QPushButton *pushButton_remove; - //! @} - - void newMisc(QString folder); -public slots: - //! Add the selected misc to the current recipe. - void addMisc(const QModelIndex& = QModelIndex()); - //! Delete selected misc from the database. - void removeMisc(); - //! Bring up the editor for the selected misc. - void editSelected(); - //! Add a new misc to the database. - void newMisc(); - //! Filter out the matching miscs. - void filterMisc(QString searchExpression); - -protected: - - virtual void changeEvent(QEvent* event) - { - if(event->type() == QEvent::LanguageChange) - retranslateUi(); - QDialog::changeEvent(event); - } - -private: - MainWindow* mainWindow; - MiscTableModel* miscTableModel; - MiscSortFilterProxyModel* miscTableProxy; - int numMiscs; - MiscEditor* miscEdit; - - void doLayout(); - void retranslateUi(); -}; - -#endif diff --git a/src/MiscEditor.cpp b/src/MiscEditor.cpp deleted file mode 100644 index 21be4328..00000000 --- a/src/MiscEditor.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/*====================================================================================================================== - * MiscEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Matt Young - * • Mik Firestone - * • Philip Greggory Lee - * • Samuel Östling - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "MiscEditor.h" - -#include -#include -#include - -#include "BtHorizontalTabs.h" -#include "config.h" -#include "database/ObjectStoreWrapper.h" -#include "measurement/Unit.h" -#include "model/Misc.h" - -MiscEditor::MiscEditor(QWidget * parent) : - QDialog(parent), - obsMisc(nullptr) { - setupUi(this); - - tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs); - - connect(pushButton_new, SIGNAL(clicked()), this, SLOT(newMisc())); - connect(pushButton_save, &QAbstractButton::clicked, this, &MiscEditor::save); - connect(pushButton_cancel, &QAbstractButton::clicked, this, &MiscEditor::clearAndClose); - - return; -} - -void MiscEditor::setMisc(Misc * m) { - if (obsMisc) { - disconnect(obsMisc, nullptr, this, nullptr); - } - - obsMisc = m; - if (obsMisc) { - connect(obsMisc, SIGNAL(changed(QMetaProperty, QVariant)), this, SLOT(changed(QMetaProperty, QVariant))); - showChanges(); - } - return; -} - -void MiscEditor::save() { - if (this->obsMisc == nullptr) { - setVisible(false); - return; - } - - qDebug() << Q_FUNC_INFO << comboBox_type->currentIndex(); - qDebug() << Q_FUNC_INFO << comboBox_use->currentIndex(); - - 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->toCanonical().quantity()); - this->obsMisc->setAmountIsWeight(checkBox_isWeight->checkState() == Qt::Checked); - this->obsMisc->setUseFor (textEdit_useFor->toPlainText()); - this->obsMisc->setNotes (textEdit_notes->toPlainText()); - - if (this->obsMisc->key() < 0) { - qDebug() << Q_FUNC_INFO << "Inserting into database"; - ObjectStoreWrapper::insert(*this->obsMisc); - } - // do this late to make sure we've the row in the inventory table - this->obsMisc->setInventoryAmount(lineEdit_inventory->toCanonical().quantity()); - setVisible(false); - return; -} - -void MiscEditor::clearAndClose() { - setMisc(nullptr); - setVisible(false); // Hide the window. -} - -void MiscEditor::changed(QMetaProperty prop, QVariant /*val*/) { - if (sender() == obsMisc) { - showChanges(&prop); - } -} - -void MiscEditor::showChanges(QMetaProperty * metaProp) { - if (obsMisc == nullptr) { - return; - } - - QString propName; - QVariant value; - bool updateAll = false; - if (metaProp == nullptr) { - updateAll = true; - } else { - propName = metaProp->name(); - value = metaProp->read(obsMisc); - } - - if (propName == PropertyNames::NamedEntity::name || updateAll) { - lineEdit_name->setText(obsMisc->name()); - lineEdit_name->setCursorPosition(0); - tabWidget_editor->setTabText(0, obsMisc->name()); - if (!updateAll) { - return; - } - } - if (propName == PropertyNames::Misc::type || updateAll) { - comboBox_type->setCurrentIndex(static_cast(obsMisc->type())); - if (!updateAll) { - return; - } - } - if (propName == PropertyNames::Misc::use || updateAll) { - comboBox_use->setCurrentIndex(static_cast(obsMisc->use())); - if (!updateAll) { - return; - } - } - if (propName == PropertyNames::Misc::time || updateAll) { - lineEdit_time->setText(obsMisc); - if (!updateAll) { - return; - } - } - if (propName == PropertyNames::Misc::amountIsWeight || updateAll) { - qDebug() << Q_FUNC_INFO; - checkBox_isWeight->setCheckState(obsMisc->amountIsWeight() ? Qt::Checked : Qt::Unchecked); - if (!updateAll) { - return; - } - } - if (propName == PropertyNames::NamedEntityWithInventory::inventory || updateAll) { - lineEdit_inventory->setText(obsMisc); - if (!updateAll) { - return; - } - } - if (propName == PropertyNames::Misc::useFor || updateAll) { - textEdit_useFor->setPlainText(obsMisc->useFor()); - if (!updateAll) { - return; - } - } - if (propName == PropertyNames::Misc::notes || updateAll) { - textEdit_notes->setPlainText(obsMisc->notes()); - if (!updateAll) { - return; - } - } -} - -void MiscEditor::newMisc(QString folder) { - QString name = QInputDialog::getText(this, tr("Misc name"), tr("Misc name:")); - if (name.isEmpty()) { - return; - } - - // .:TODO:. This leads to a memory leak in clearAndClose(). Change to shared_ptr - Misc * m = new Misc(name); - - if (! folder.isEmpty()) { - m->setFolder(folder); - } - - setMisc(m); - show(); - return; -} - -void MiscEditor::newMisc() { - newMisc(QString()); - return; -} diff --git a/src/MiscSortFilterProxyModel.cpp b/src/MiscSortFilterProxyModel.cpp index 97385e91..d18994ea 100644 --- a/src/MiscSortFilterProxyModel.cpp +++ b/src/MiscSortFilterProxyModel.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * MiscSortFilterProxyModel.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * MiscSortFilterProxyModel.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Daniel Pettersson * • Matt Young * • Mik Firestone @@ -42,8 +42,9 @@ bool MiscSortFilterProxyModel::lessThan(const QModelIndex &left, rightMisc = source->data(right); } - switch (left.column()) { - case MISCINVENTORYCOL: + auto const columnIndex = static_cast(left.column()); + switch (columnIndex) { + case MiscTableModel::ColumnIndex::Inventory: if (Measurement::qStringToSI(leftMisc.toString(), Measurement::PhysicalQuantity::Mass).quantity() == 0.0 && this->sortOrder() == Qt::AscendingOrder) { return false; @@ -51,11 +52,11 @@ bool MiscSortFilterProxyModel::lessThan(const QModelIndex &left, return (Measurement::qStringToSI(leftMisc.toString(), Measurement::PhysicalQuantity::Mass) < Measurement::qStringToSI(rightMisc.toString(), Measurement::PhysicalQuantity::Mass)); - case MISCAMOUNTCOL: + case MiscTableModel::ColumnIndex::Amount: return (Measurement::qStringToSI(leftMisc.toString(), Measurement::PhysicalQuantity::Mass) < Measurement::qStringToSI(rightMisc.toString(), Measurement::PhysicalQuantity::Mass)); - case MISCTIMECOL: + case MiscTableModel::ColumnIndex::Time: return (Measurement::qStringToSI(leftMisc.toString(), Measurement::PhysicalQuantity::Time) < Measurement::qStringToSI(rightMisc.toString(), Measurement::PhysicalQuantity::Time)); diff --git a/src/OgAdjuster.cpp b/src/OgAdjuster.cpp index 67abee5c..a202aab0 100644 --- a/src/OgAdjuster.cpp +++ b/src/OgAdjuster.cpp @@ -21,16 +21,26 @@ #include "OgAdjuster.h" #include "Algorithms.h" +#include "measurement/Measurement.h" #include "measurement/Unit.h" #include "model/Equipment.h" #include "model/Recipe.h" -OgAdjuster::OgAdjuster( QWidget* parent ) : QDialog(parent) { +OgAdjuster::OgAdjuster( QWidget* parent ) : + QDialog{parent}, + recObs {nullptr} { setupUi(this); - recObs = 0; + SMART_FIELD_INIT_FIXED(OgAdjuster, label_sg , lineEdit_sg , double, Measurement::Units::specificGravity , 3); // Input: SG + SMART_FIELD_INIT_FS (OgAdjuster, label_temp , lineEdit_temp , double, Measurement::PhysicalQuantity::Temperature, 1); // Input: Temp + SMART_FIELD_INIT_FS (OgAdjuster, label_calTemp , lineEdit_calTemp , double, Measurement::PhysicalQuantity::Temperature, 1); // Input: Calibration Temp + SMART_FIELD_INIT_FIXED(OgAdjuster, label_plato , lineEdit_plato , double, Measurement::Units::plato , 1); // Input: Plato + SMART_FIELD_INIT_FS (OgAdjuster, label_volume , lineEdit_volume , double, Measurement::PhysicalQuantity::Volume ); // Input: Pre-Boil Volume + SMART_FIELD_INIT_FIXED(OgAdjuster, label_og , lineEdit_og , double, Measurement::Units::specificGravity , 3); // Output: OG w/o Correction + SMART_FIELD_INIT_FS (OgAdjuster, label_add , lineEdit_add , double, Measurement::PhysicalQuantity::Volume ); // Output: Add to Boil + SMART_FIELD_INIT_FS (OgAdjuster, label_batchSize, lineEdit_batchSize, double, Measurement::PhysicalQuantity::Volume ); // Output: Final Batch Size - connect( pushButton_calculate, &QAbstractButton::clicked, this, &OgAdjuster::calculate ); + connect(this->pushButton_calculate, &QAbstractButton::clicked, this, &OgAdjuster::calculate ); return; } @@ -47,8 +57,8 @@ void OgAdjuster::calculate() { // Get inputs. double sg = lineEdit_sg->toCanonical().quantity(); - bool okPlato = true; - double plato = lineEdit_plato->toDoubleRaw(&okPlato); + bool okPlato = true; + double plato = Measurement::extractRawFromString(lineEdit_plato->text(), &okPlato); double temp_c = lineEdit_temp->toCanonical().quantity(); double hydroTemp_c = lineEdit_calTemp->toCanonical().quantity(); double wort_l = lineEdit_volume->toCanonical().quantity(); @@ -73,17 +83,14 @@ void OgAdjuster::calculate() { // Calculate missing input parameters. double sg_20C = 0.0; - if( gotSG ) - { + if (gotSG) { double sg_15C = sg * Algorithms::getWaterDensity_kgL(hydroTemp_c)/Algorithms::getWaterDensity_kgL(15) + Algorithms::hydrometer15CCorrection( temp_c ); sg_20C = sg_15C * Algorithms::getWaterDensity_kgL(15)/Algorithms::getWaterDensity_kgL(20); - plato = Algorithms::SG_20C20C_toPlato( sg_20C ); - lineEdit_plato->setText( sg_20C ); //Event if the display is in Plato, we must send it in default unit - } - else - { - sg_20C = Algorithms::PlatoToSG_20C20C( plato ); + plato = Algorithms::SG_20C20C_toPlato(sg_20C); + lineEdit_plato->setAmount(sg_20C); // Event if the display is in Plato, we must send it in default unit + } else { + sg_20C = Algorithms::PlatoToSG_20C20C(plato); } // Calculate intermediate parameters. @@ -115,8 +122,8 @@ void OgAdjuster::calculate() { finalVolume_l += waterToAdd_l; // Display output. - lineEdit_og->setText(finalUncorrectedSg_20C); - lineEdit_add->setText(waterToAdd_l); - lineEdit_batchSize->setText(finalVolume_l); + this->lineEdit_og ->setAmount(finalUncorrectedSg_20C); + this->lineEdit_add ->setAmount(waterToAdd_l); + this->lineEdit_batchSize->setAmount(finalVolume_l); return; } diff --git a/src/OptionDialog.cpp b/src/OptionDialog.cpp index 945cdcf9..c7bcd309 100644 --- a/src/OptionDialog.cpp +++ b/src/OptionDialog.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * OptionDialog.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * OptionDialog.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Daniel Pettersson * • Greg Meess @@ -40,7 +40,6 @@ #include #include -#include "BtLineEdit.h" #include "database/Database.h" #include "Localization.h" #include "Logging.h" diff --git a/src/PitchDialog.cpp b/src/PitchDialog.cpp index 1b1d1f32..7b1940ce 100644 --- a/src/PitchDialog.cpp +++ b/src/PitchDialog.cpp @@ -33,24 +33,30 @@ PitchDialog::PitchDialog(QWidget* parent) : QDialog(parent) { setupUi(this); // Set default dates - dateEdit_ProductionDate->setMaximumDate(QDate::currentDate()); - dateEdit_ProductionDate->setDate(QDate::currentDate()); - updateViabilityFromDate(QDate::currentDate()); - - connect(lineEdit_vol, &BtLineEdit::textModified, this, &PitchDialog::calculate); - connect(lineEdit_OG, &BtLineEdit::textModified, this, &PitchDialog::calculate); - connect(slider_pitchRate, &QAbstractSlider::valueChanged, this, &PitchDialog::calculate); - connect(slider_pitchRate, &QAbstractSlider::valueChanged, this, &PitchDialog::updateShownPitchRate); - connect(spinBox_Viability, QOverload::of(&QSpinBox::valueChanged), this, &PitchDialog::calculate); - connect(spinBox_VialsPitched, QOverload::of(&QSpinBox::valueChanged), this, &PitchDialog::calculate); - connect(comboBox_AerationMethod, QOverload::of(&QComboBox::currentIndexChanged), this, &PitchDialog::calculate); - connect(dateEdit_ProductionDate, &QDateTimeEdit::dateChanged, this, &PitchDialog::updateViabilityFromDate); - connect(checkBox_CalculateViability, &QCheckBox::stateChanged, this, &PitchDialog::toggleViabilityFromDate); + this->dateEdit_ProductionDate->setMaximumDate(QDate::currentDate()); + this->dateEdit_ProductionDate->setDate(QDate::currentDate()); + this->updateViabilityFromDate(QDate::currentDate()); + + SMART_FIELD_INIT_FS(PitchDialog, label_vol , lineEdit_vol , double, Measurement::PhysicalQuantity::Volume ); // Input: Wort Volume + SMART_FIELD_INIT_FS(PitchDialog, label_og , lineEdit_OG , double, Measurement::PhysicalQuantity::Density ); // Input: OG + SMART_FIELD_INIT_FS(PitchDialog, label_cells , lineEdit_cells , double, NonPhysicalQuantity::Dimensionless, 1); // Output: Billions of Yeast Cells Required + SMART_FIELD_INIT_FS(PitchDialog, label_vials , lineEdit_vials , double, NonPhysicalQuantity::Dimensionless, 0); // Output: # Vials/Smack Packs w/o Starter + SMART_FIELD_INIT_FS(PitchDialog, label_yeast , lineEdit_yeast , double, Measurement::PhysicalQuantity::Mass ); // Output: Dry Yeast + SMART_FIELD_INIT_FS(PitchDialog, label_starterVol, lineEdit_starterVol, double, Measurement::PhysicalQuantity::Volume ); // Output: Starter Volume + + connect(this->lineEdit_vol, &SmartLineEdit::textModified, this, &PitchDialog::calculate ); + connect(this->lineEdit_OG, &SmartLineEdit::textModified, this, &PitchDialog::calculate ); + connect(this->slider_pitchRate, &QAbstractSlider::valueChanged, this, &PitchDialog::calculate ); + connect(this->slider_pitchRate, &QAbstractSlider::valueChanged, this, &PitchDialog::updateShownPitchRate ); + connect(this->spinBox_Viability, QOverload::of(&QSpinBox::valueChanged), this, &PitchDialog::calculate ); + connect(this->spinBox_VialsPitched, QOverload::of(&QSpinBox::valueChanged), this, &PitchDialog::calculate ); + connect(this->comboBox_AerationMethod, QOverload::of(&QComboBox::currentIndexChanged), this, &PitchDialog::calculate ); + connect(this->dateEdit_ProductionDate, &QDateTimeEdit::dateChanged, this, &PitchDialog::updateViabilityFromDate); + connect(this->checkBox_CalculateViability, &QCheckBox::stateChanged, this, &PitchDialog::toggleViabilityFromDate); // Dates are a little more cranky -/// connect(label_productionDate, &BtLabel::changedSystemOfMeasurementOrScale, this, &PitchDialog::updateProductionDate); this->updateProductionDate(); - updateShownPitchRate(0); + this->updateShownPitchRate(0); return; } @@ -69,11 +75,11 @@ void PitchDialog::updateProductionDate() { } void PitchDialog::setWortVolume_l(double volume) { - lineEdit_vol->setText(volume); + this->lineEdit_vol->setAmount(volume); } void PitchDialog::setWortDensity(double sg) { - lineEdit_OG->setText(sg); + this->lineEdit_OG->setAmount(sg); } void PitchDialog::calculate() { @@ -94,7 +100,7 @@ void PitchDialog::calculate() { double dry_g = cells / 20e9; // 20 billion cells per dry gram. // Set the maximum number of vials pitched based on # of vials needed without a starter. - spinBox_VialsPitched->setMaximum(vials < 1 ? 1 : floor(vials)); + this->spinBox_VialsPitched->setMaximum(vials < 1 ? 1 : floor(vials)); // Set the aeration factor for the starter size double aerationFactor; @@ -117,10 +123,10 @@ void PitchDialog::calculate() { double inoculationRate = pow((12.522 / growthRate), 2.18); double starterVol_l = totalCellsPitched / (inoculationRate * aerationFactor); - lineEdit_cells->setText(cells/1e9, 1); - lineEdit_starterVol->setText(starterVol_l); - lineEdit_yeast->setText(dry_g/1000); //Needs to be converted into default unit (kg) - lineEdit_vials->setText(vials,0); + this->lineEdit_cells ->setAmount(cells/1e9); + this->lineEdit_starterVol->setAmount(starterVol_l); + this->lineEdit_yeast ->setAmount(dry_g/1000); //Needs to be converted into default unit (kg) + this->lineEdit_vials ->setAmount(vials); return; } @@ -129,7 +135,7 @@ void PitchDialog::updateShownPitchRate(int percent) { double rate_MpermLP = (2-0.75) * ((double)percent) / 100.0 + 0.75; // NOTE: We are changing the LABEL here, not the LineEdit. Leave it be - label_pitchRate->setText( QString("%L1").arg(rate_MpermLP, 1, 'f', 2, QChar('0')) ); + this->label_pitchRate->setText( QString("%L1").arg(rate_MpermLP, 1, 'f', 2, QChar('0')) ); return; } @@ -141,14 +147,14 @@ void PitchDialog::toggleViabilityFromDate(int state) { if (state == Qt::Unchecked) { // If the box is not checked, disable the date and allow // the user to manually set the viability. - spinBox_Viability->setEnabled(true); - dateEdit_ProductionDate->setEnabled(false); + this->spinBox_Viability->setEnabled(true); + this->dateEdit_ProductionDate->setEnabled(false); } else if (state == Qt::Checked) { // If the box is checked, prevent the user from manually setting // the viability. Use the date editor instead. - spinBox_Viability->setEnabled(false); - dateEdit_ProductionDate->setEnabled(true); - updateViabilityFromDate(dateEdit_ProductionDate->date()); + this->spinBox_Viability->setEnabled(false); + this->dateEdit_ProductionDate->setEnabled(true); + this->updateViabilityFromDate(dateEdit_ProductionDate->date()); } return; } @@ -160,6 +166,6 @@ void PitchDialog::updateViabilityFromDate(QDate date) { // Set the viability based on the number of days since the yeast // production date. int daysDifference = date.daysTo(QDate::currentDate()); - spinBox_Viability->setValue(97 - 0.7 * daysDifference); + this->spinBox_Viability->setValue(97 - 0.7 * daysDifference); return; } diff --git a/src/PrimingDialog.cpp b/src/PrimingDialog.cpp index 247dadef..12d13244 100644 --- a/src/PrimingDialog.cpp +++ b/src/PrimingDialog.cpp @@ -38,6 +38,11 @@ PrimingDialog::PrimingDialog(QWidget* parent) : QDialog(parent) { this->sugarGroup->addButton(radioButton_sucrose); this->sugarGroup->addButton(radioButton_dme); + SMART_FIELD_INIT_FS(PrimingDialog, label_beerVol, lineEdit_beerVol, double, Measurement::PhysicalQuantity::Volume ); + SMART_FIELD_INIT_FS(PrimingDialog, label_temp , lineEdit_temp , double, Measurement::PhysicalQuantity::Temperature, 1); + SMART_FIELD_INIT_FS(PrimingDialog, label_vols , lineEdit_vols , double, Measurement::PhysicalQuantity::Carbonation, 1); + SMART_FIELD_INIT_FS(PrimingDialog, label_output , lineEdit_output , double, Measurement::PhysicalQuantity::Mass ); + connect(pushButton_calculate, &QAbstractButton::clicked, this, &PrimingDialog::calculate); return; } @@ -46,17 +51,17 @@ PrimingDialog::~PrimingDialog() = default; void PrimingDialog::calculate() { - double beer_l = lineEdit_beerVol->toCanonical().quantity(); - double temp_c = lineEdit_temp->toCanonical().quantity(); - double desiredVols = lineEdit_vols->toCanonical().quantity(); + double const beer_l = lineEdit_beerVol->toCanonical().quantity(); + double const temp_c = lineEdit_temp ->toCanonical().quantity(); + double const desiredVols = lineEdit_vols ->toCanonical().quantity(); qDebug() << Q_FUNC_INFO << "Beer volume (liters):" << beer_l << ", Temp (°C):" << temp_c << ", Desired Volumes:" << desiredVols; - double residualVols = 1.57 * pow(0.97, temp_c); // Amount of CO2 still in suspension. - double addedVols = desiredVols - residualVols; - double co2_l = addedVols * beer_l; // Liters of CO2 we need to generate (at 273 K and 1 atm). - double co2_mol = co2_l / 22.4; // Mols of CO2 we need. + double const residualVols = 1.57 * pow(0.97, temp_c); // Amount of CO2 still in suspension. + double const addedVols = desiredVols - residualVols; + double const co2_l = addedVols * beer_l; // Liters of CO2 we need to generate (at 273 K and 1 atm). + double const co2_mol = co2_l / 22.4; // Mols of CO2 we need. double sugar_mol; double sugar_g; @@ -83,9 +88,9 @@ void PrimingDialog::calculate() { return; } - // The amount have to be set in default unit to BtLineEdit. + // The amount have to be set in default unit to SmartLineEdit. // We should find a better solution, but until it is not, we must do it this way. - lineEdit_output->setText( sugar_g/1000 ); + lineEdit_output->setAmount(sugar_g/1000); return; } diff --git a/src/RadarChart.cpp b/src/RadarChart.cpp index f7bb594b..7de29266 100644 --- a/src/RadarChart.cpp +++ b/src/RadarChart.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * RadarChart.cpp is part of Brewken, and is copyright the following authors 2021-2022: + * RadarChart.cpp is part of Brewken, and is copyright the following authors 2021-2023: * • Matt Young * * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -16,12 +16,26 @@ #include "RadarChart.h" #include -// We need an extra define on Windows to access the M_PI constant in cmath. (More details in comment below.) -#ifdef Q_OS_WIN -#define _USE_MATH_DEFINES -#endif #include -//#include Uncomment this when we switch to C++20 + +// +// C++20 introduces std::numbers::pi, which has better cross-platform support than the M_PI constant in cmath. However, +// older versions of GCC (eg as shipped with Ubuntu 20.04 LTS) do not ship with the new header. +// +#if defined(__linux__ ) && defined(__GNUC__) && (__GNUC__ < 10) + +// I hope this is allowed. We'll be able to get rid of it once we stop needing to support old versions of GCC +namespace std { + namespace numbers { + constexpr double pi = M_PI; + } +} + +#else + +#include // For std::numbers::pi + +#endif #include #include @@ -39,12 +53,7 @@ namespace { // // For functions, angles are all in radians, and the "natural" coordinate system measures anti-clockwise // starting from 3 o'clock as 0rad. There are 2π radians in a circle. - // - // When we switch to C++20, we should replace M_PI (non-standard but usually defined in cmath) with std::numbers::pi - // (standard as of C++20). Note that, on Windows, - // per https://docs.microsoft.com/en-us/cpp/c-runtime-library/math-constants?view=msvc-160, we also have to - // #define _USE_MATH_DEFINES to use M_PI. - double const RadiansInACircle = 2 * M_PI; + double const RadiansInACircle = 2 * std::numbers::pi; double const StartingAngleInRadians = RadiansInACircle / 4; diff --git a/src/RecipeExtrasWidget.cpp b/src/RecipeExtrasWidget.cpp index e5bfcdf9..3d51b56f 100644 --- a/src/RecipeExtrasWidget.cpp +++ b/src/RecipeExtrasWidget.cpp @@ -23,32 +23,54 @@ #include #include -#include "BtLabel.h" #include "MainWindow.h" #include "measurement/Unit.h" #include "model/Recipe.h" -RecipeExtrasWidget::RecipeExtrasWidget(QWidget* parent) : QWidget(parent), recipe(nullptr) { - setupUi(this); - - ratingChanged = false; - - connect(lineEdit_age, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updateAge ); - connect(lineEdit_ageTemp, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updateAgeTemp ); - connect(lineEdit_asstBrewer, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updateBrewerAsst ); - connect(lineEdit_brewer, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updateBrewer ); - connect(lineEdit_carbVols, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updateCarbonation ); - connect(lineEdit_primaryAge, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updatePrimaryAge ); - connect(lineEdit_primaryTemp, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updatePrimaryTemp ); - connect(lineEdit_secAge, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updateSecondaryAge ); - connect(lineEdit_secTemp, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updateSecondaryTemp); - connect(lineEdit_tertAge, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updateTertiaryAge ); - connect(lineEdit_tertTemp, &BtLineEdit::textModified , this, &RecipeExtrasWidget::updateTertiaryTemp ); - connect(spinBox_tasteRating, QOverload::of(&QSpinBox::valueChanged), this, &RecipeExtrasWidget::changeRatings ); - connect(spinBox_tasteRating, &QAbstractSpinBox::editingFinished , this, &RecipeExtrasWidget::updateTasteRating ); - connect(dateEdit_date, &QDateTimeEdit::dateChanged , this, &RecipeExtrasWidget::updateDate ); - connect(btTextEdit_notes, &BtTextEdit::textModified , this, &RecipeExtrasWidget::updateNotes ); - connect(btTextEdit_tasteNotes, &BtTextEdit::textModified , this, &RecipeExtrasWidget::updateTasteNotes ); +RecipeExtrasWidget::RecipeExtrasWidget(QWidget* parent) : + QWidget(parent), + recipe(nullptr), + ratingChanged{false} { + + this->setupUi(this); + + // Note that label_primaryAge, label_secAge, label_tertAge, label_age are QLabel, not SmartLabel, as we're "forcing" + // the measurement to be in days rather than allowing the usual units of PhysicalQuantity::Time + SMART_FIELD_INIT(RecipeExtrasWidget, label_brewer , lineEdit_brewer , Recipe, PropertyNames::Recipe::brewer ); + SMART_FIELD_INIT(RecipeExtrasWidget, label_asstBrewer , lineEdit_asstBrewer , Recipe, PropertyNames::Recipe::asstBrewer ); + SMART_FIELD_INIT(RecipeExtrasWidget, label_primaryAge , lineEdit_primaryAge , Recipe, PropertyNames::Recipe::primaryAge_days , 0); + SMART_FIELD_INIT(RecipeExtrasWidget, label_primaryTemp, lineEdit_primaryTemp, Recipe, PropertyNames::Recipe::primaryTemp_c , 1); + SMART_FIELD_INIT(RecipeExtrasWidget, label_secAge , lineEdit_secAge , Recipe, PropertyNames::Recipe::secondaryAge_days, 0); + SMART_FIELD_INIT(RecipeExtrasWidget, label_secTemp , lineEdit_secTemp , Recipe, PropertyNames::Recipe::secondaryTemp_c , 1); + SMART_FIELD_INIT(RecipeExtrasWidget, label_tertAge , lineEdit_tertAge , Recipe, PropertyNames::Recipe::tertiaryAge_days , 0); + SMART_FIELD_INIT(RecipeExtrasWidget, label_tertTemp , lineEdit_tertTemp , Recipe, PropertyNames::Recipe::tertiaryTemp_c , 1); + SMART_FIELD_INIT(RecipeExtrasWidget, label_age , lineEdit_age , Recipe, PropertyNames::Recipe::age_days , 0); + SMART_FIELD_INIT(RecipeExtrasWidget, label_ageTemp , lineEdit_ageTemp , Recipe, PropertyNames::Recipe::ageTemp_c , 1); + SMART_FIELD_INIT(RecipeExtrasWidget, label_carbVols , lineEdit_carbVols , Recipe, PropertyNames::Recipe::carbonation_vols ); + + // See comment in model/Recipe.cpp about things we measure in days. If we switched them from Dimensionless to Time, + // we would need something like this + // this->lineEdit_primaryAge->getSmartField().setForcedRelativeScale(Measurement::UnitSystem::RelativeScale::Large); + // this->lineEdit_secAge ->getSmartField().setForcedRelativeScale(Measurement::UnitSystem::RelativeScale::Large); + // this->lineEdit_tertAge ->getSmartField().setForcedRelativeScale(Measurement::UnitSystem::RelativeScale::Large); + // this->lineEdit_age ->getSmartField().setForcedRelativeScale(Measurement::UnitSystem::RelativeScale::Large); + + connect(this->lineEdit_age , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateAge ); + connect(this->lineEdit_ageTemp , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateAgeTemp ); + connect(this->lineEdit_asstBrewer , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateBrewerAsst ); + connect(this->lineEdit_brewer , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateBrewer ); + connect(this->lineEdit_carbVols , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateCarbonation ); + connect(this->lineEdit_primaryAge , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updatePrimaryAge ); + connect(this->lineEdit_primaryTemp , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updatePrimaryTemp ); + connect(this->lineEdit_secAge , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateSecondaryAge ); + connect(this->lineEdit_secTemp , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateSecondaryTemp); + connect(this->lineEdit_tertAge , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateTertiaryAge ); + connect(this->lineEdit_tertTemp , &SmartLineEdit::textModified , this, &RecipeExtrasWidget::updateTertiaryTemp ); + connect(this->spinBox_tasteRating , QOverload::of(&QSpinBox::valueChanged), this, &RecipeExtrasWidget::changeRatings ); + connect(this->spinBox_tasteRating , &QAbstractSpinBox::editingFinished , this, &RecipeExtrasWidget::updateTasteRating ); + connect(this->dateEdit_date , &QDateTimeEdit::dateChanged , this, &RecipeExtrasWidget::updateDate ); + connect(this->btTextEdit_notes , &BtTextEdit::textModified , this, &RecipeExtrasWidget::updateNotes ); + connect(this->btTextEdit_tasteNotes, &BtTextEdit::textModified , this, &RecipeExtrasWidget::updateTasteNotes ); return; } @@ -67,14 +89,14 @@ void RecipeExtrasWidget::setRecipe(Recipe* rec) { void RecipeExtrasWidget::updateBrewer() { if (!this->recipe) { return;} - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::brewer, lineEdit_brewer->text(), tr("Change Brewer")); + MainWindow::instance().doOrRedoUpdate(*recipe, TYPE_INFO(Recipe, brewer), lineEdit_brewer->text(), tr("Change Brewer")); return; } void RecipeExtrasWidget::updateBrewerAsst() { if (!this->recipe) { return;} if ( lineEdit_asstBrewer->isModified() ) { - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::asstBrewer, lineEdit_asstBrewer->text(), tr("Change Assistant Brewer")); + MainWindow::instance().doOrRedoUpdate(*recipe, TYPE_INFO(Recipe, asstBrewer), lineEdit_asstBrewer->text(), tr("Change Assistant Brewer")); } return; } @@ -85,7 +107,7 @@ void RecipeExtrasWidget::updateTasteRating() { if (!this->recipe) { return;} if ( ratingChanged ) { - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::tasteRating, spinBox_tasteRating->value(), tr("Change Taste Rating")); + MainWindow::instance().doOrRedoUpdate(*recipe, TYPE_INFO(Recipe, tasteRating), spinBox_tasteRating->value(), tr("Change Taste Rating")); ratingChanged = false; } return; @@ -93,55 +115,55 @@ void RecipeExtrasWidget::updateTasteRating() { void RecipeExtrasWidget::updatePrimaryAge() { if (!this->recipe) { return;} - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::primaryAge_days, lineEdit_primaryAge->toCanonical().quantity(), tr("Change Primary Age")); + MainWindow::instance().doOrRedoUpdate(*recipe, TYPE_INFO(Recipe, primaryAge_days), lineEdit_primaryAge->toCanonical().quantity(), tr("Change Primary Age")); } void RecipeExtrasWidget::updatePrimaryTemp() { if (!this->recipe) { return;} - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::primaryTemp_c, lineEdit_primaryTemp->toCanonical().quantity(), tr("Change Primary Temp")); + MainWindow::instance().doOrRedoUpdate(*recipe, TYPE_INFO(Recipe, primaryTemp_c), lineEdit_primaryTemp->toCanonical().quantity(), tr("Change Primary Temp")); } void RecipeExtrasWidget::updateSecondaryAge() { if (!this->recipe) { return;} - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::secondaryAge_days, lineEdit_secAge->toCanonical().quantity(), tr("Change Secondary Age")); + MainWindow::instance().doOrRedoUpdate(*recipe, TYPE_INFO(Recipe, secondaryAge_days), lineEdit_secAge->toCanonical().quantity(), tr("Change Secondary Age")); } void RecipeExtrasWidget::updateSecondaryTemp() { if (!this->recipe) { return;} - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::secondaryTemp_c, lineEdit_secTemp->toCanonical().quantity(), tr("Change Secondary Temp")); + MainWindow::instance().doOrRedoUpdate(*recipe, TYPE_INFO(Recipe, secondaryTemp_c), lineEdit_secTemp->toCanonical().quantity(), tr("Change Secondary Temp")); } void RecipeExtrasWidget::updateTertiaryAge() { if (!this->recipe) { return;} - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::tertiaryAge_days, lineEdit_tertAge->toCanonical().quantity(), tr("Change Tertiary Age")); + MainWindow::instance().doOrRedoUpdate(*recipe, TYPE_INFO(Recipe, tertiaryAge_days), lineEdit_tertAge->toCanonical().quantity(), tr("Change Tertiary Age")); } void RecipeExtrasWidget::updateTertiaryTemp() { if (!this->recipe) { return;} - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::tertiaryTemp_c, lineEdit_tertTemp->toCanonical().quantity(), tr("Change Tertiary Temp")); + MainWindow::instance().doOrRedoUpdate(*recipe, TYPE_INFO(Recipe, tertiaryTemp_c), lineEdit_tertTemp->toCanonical().quantity(), tr("Change Tertiary Temp")); } void RecipeExtrasWidget::updateAge() { if (!this->recipe) { return;} - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::age_days, lineEdit_age->toCanonical().quantity(), tr("Change Age")); + MainWindow::instance().doOrRedoUpdate(*recipe, TYPE_INFO(Recipe, age_days), lineEdit_age->toCanonical().quantity(), tr("Change Age")); } void RecipeExtrasWidget::updateAgeTemp() { if (!this->recipe) { return;} - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::ageTemp_c, lineEdit_ageTemp->toCanonical().quantity(), tr("Change Age Temp")); + MainWindow::instance().doOrRedoUpdate(*recipe, TYPE_INFO(Recipe, ageTemp_c), lineEdit_ageTemp->toCanonical().quantity(), tr("Change Age Temp")); } void RecipeExtrasWidget::updateDate(QDate const & date) { if (!this->recipe) { return;} if (date.isNull()) { - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::date, dateEdit_date->date(), tr("Change Date")); + MainWindow::instance().doOrRedoUpdate(*recipe, TYPE_INFO(Recipe, date), dateEdit_date->date(), tr("Change Date")); } else { // We have to be careful to avoid going round in circles here. When we call // this->dateEdit_date->setDate(this->recipe->date()) to show the Recipe date in the UI, that will generate a // signal that ends up calling this function to say the date on the Recipe has changed, which it hasn't. if (date != this->recipe->date()) { - MainWindow::instance().doOrRedoUpdate(*recipe, PropertyNames::Recipe::date, date, tr("Change Date")); + MainWindow::instance().doOrRedoUpdate(*recipe, TYPE_INFO(Recipe, date), date, tr("Change Date")); } } return; @@ -151,7 +173,7 @@ void RecipeExtrasWidget::updateCarbonation() { if (!this->recipe) { return;} MainWindow::instance().doOrRedoUpdate(*recipe, - PropertyNames::Recipe::carbonation_vols, + TYPE_INFO(Recipe, carbonation_vols), lineEdit_carbVols->toCanonical().quantity(), tr("Change Carbonation")); } @@ -160,7 +182,7 @@ void RecipeExtrasWidget::updateTasteNotes() { if (!this->recipe) { return;} MainWindow::instance().doOrRedoUpdate(*recipe, - PropertyNames::Recipe::tasteNotes, + TYPE_INFO(Recipe, tasteNotes), btTextEdit_tasteNotes->toPlainText(), tr("Edit Taste Notes")); } @@ -169,7 +191,7 @@ void RecipeExtrasWidget::updateNotes() { if (!this->recipe) { return;} MainWindow::instance().doOrRedoUpdate(*recipe, - PropertyNames::Recipe::notes, + TYPE_INFO(Recipe, notes), btTextEdit_notes->toPlainText(), tr("Edit Notes")); } @@ -225,21 +247,21 @@ void RecipeExtrasWidget::showChanges(QMetaProperty* prop) { // "change is made" ... rinse, lather, repeat // Unlike other editors, this one needs to read from recipe when it gets an // updateAll - if (updateAll || propName == PropertyNames::Recipe::age_days ) { lineEdit_age ->setText (recipe->age_days ()); } - if (updateAll || propName == PropertyNames::Recipe::ageTemp_c ) { lineEdit_ageTemp ->setText (recipe->ageTemp_c ()); } - if (updateAll || propName == PropertyNames::Recipe::asstBrewer ) { lineEdit_asstBrewer ->setText (recipe->asstBrewer ()); } - if (updateAll || propName == PropertyNames::Recipe::brewer ) { lineEdit_brewer ->setText (recipe->brewer ()); } - if (updateAll || propName == PropertyNames::Recipe::carbonation_vols ) { lineEdit_carbVols ->setText (recipe->carbonation_vols ()); } - if (updateAll || propName == PropertyNames::Recipe::primaryAge_days ) { lineEdit_primaryAge ->setText (recipe->primaryAge_days ()); } - if (updateAll || propName == PropertyNames::Recipe::primaryTemp_c ) { lineEdit_primaryTemp ->setText (recipe->primaryTemp_c ()); } - if (updateAll || propName == PropertyNames::Recipe::secondaryAge_days) { lineEdit_secAge ->setText (recipe->secondaryAge_days()); } - if (updateAll || propName == PropertyNames::Recipe::secondaryTemp_c ) { lineEdit_secTemp ->setText (recipe->secondaryTemp_c ()); } - if (updateAll || propName == PropertyNames::Recipe::tertiaryAge_days ) { lineEdit_tertAge ->setText (recipe->tertiaryAge_days ()); } - if (updateAll || propName == PropertyNames::Recipe::tertiaryTemp_c ) { lineEdit_tertTemp ->setText (recipe->tertiaryTemp_c ()); } - if (updateAll || propName == PropertyNames::Recipe::tasteRating ) { spinBox_tasteRating ->setValue (recipe->tasteRating ()); } - if (updateAll || propName == PropertyNames::Recipe::date ) { dateEdit_date ->setDate (recipe->date ()); } - if (updateAll || propName == PropertyNames::Recipe::notes ) { btTextEdit_notes ->setPlainText(recipe->notes ()); } - if (updateAll || propName == PropertyNames::Recipe::tasteNotes ) { btTextEdit_tasteNotes->setPlainText(recipe->tasteNotes ()); } + if (updateAll || propName == PropertyNames::Recipe::age_days ) { this->lineEdit_age ->setAmount (recipe->age_days ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::ageTemp_c ) { this->lineEdit_ageTemp ->setAmount (recipe->ageTemp_c ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::asstBrewer ) { this->lineEdit_asstBrewer ->setText (recipe->asstBrewer ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::brewer ) { this->lineEdit_brewer ->setText (recipe->brewer ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::carbonation_vols ) { this->lineEdit_carbVols ->setAmount (recipe->carbonation_vols ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::primaryAge_days ) { this->lineEdit_primaryAge ->setAmount (recipe->primaryAge_days ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::primaryTemp_c ) { this->lineEdit_primaryTemp ->setAmount (recipe->primaryTemp_c ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::secondaryAge_days) { this->lineEdit_secAge ->setAmount (recipe->secondaryAge_days()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::secondaryTemp_c ) { this->lineEdit_secTemp ->setAmount (recipe->secondaryTemp_c ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::tertiaryAge_days ) { this->lineEdit_tertAge ->setAmount (recipe->tertiaryAge_days ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::tertiaryTemp_c ) { this->lineEdit_tertTemp ->setAmount (recipe->tertiaryTemp_c ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::tasteRating ) { this->spinBox_tasteRating ->setValue (recipe->tasteRating ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::date ) { this->dateEdit_date ->setDate (recipe->date ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::notes ) { this->btTextEdit_notes ->setPlainText(recipe->notes ()); if (!updateAll) { return; } } + if (updateAll || propName == PropertyNames::Recipe::tasteNotes ) { this->btTextEdit_tasteNotes->setPlainText(recipe->tasteNotes ()); if (!updateAll) { return; } } return; } diff --git a/src/RecipeFormatter.cpp b/src/RecipeFormatter.cpp index 2fae6f25..2550d20c 100644 --- a/src/RecipeFormatter.cpp +++ b/src/RecipeFormatter.cpp @@ -294,16 +294,12 @@ class RecipeFormatter::impl { "%1" "%2") .arg(tr("Batch Size")) - .arg(Measurement::displayAmount(Measurement::Amount{rec->finalVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::finalVolume_l)); + .arg(Measurement::displayAmount(Measurement::Amount{rec->finalVolume_l(), Measurement::Units::liters})); body += QString("%1" "%2" "") .arg(tr("Boil Size")) - .arg(Measurement::displayAmount(Measurement::Amount{rec->boilVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilVolume_l)); + .arg(Measurement::displayAmount(Measurement::Amount{rec->boilVolume_l(), Measurement::Units::liters})); // Second row: Boil Time and Efficiency body += QString("" "%1" @@ -312,9 +308,7 @@ class RecipeFormatter::impl { .arg(Measurement::displayAmount(Measurement::Amount{ rec->equipment() == nullptr ? 0.0 : rec->equipment()->boilTime_min(), Measurement::Units::minutes - }, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilTime_min)); + })); body += QString("%1" "%2") .arg(tr("Efficiency")) @@ -325,17 +319,11 @@ class RecipeFormatter::impl { "%1" "%2") .arg(tr("OG")) - .arg(Measurement::displayAmount(Measurement::Amount{rec->og(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::og, - 3)); + .arg(Measurement::displayAmount(Measurement::Amount{rec->og(), Measurement::Units::specificGravity}, 3)); body += QString("%1" "%2") .arg(tr("FG")) - .arg(Measurement::displayAmount(Measurement::Amount{rec->fg(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::fg, - 3)); + .arg(Measurement::displayAmount(Measurement::Amount{rec->fg(), Measurement::Units::specificGravity}, 3)); // Fourth row: ABV and Bitterness. We need to set the bitterness string up first body += QString("" @@ -354,10 +342,7 @@ class RecipeFormatter::impl { "%1" "%2 (%3)") .arg(tr("Color")) - .arg(Measurement::displayAmount(Measurement::Amount{rec->color_srm(), Measurement::Units::srm}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::color_srm, - 1)) + .arg(Measurement::displayAmount(Measurement::Amount{rec->color_srm(), Measurement::Units::srm}, 1)) .arg(ColorMethods::colorFormulaName()); bool displayMetricVolumes = @@ -384,34 +369,26 @@ class RecipeFormatter::impl { QStringList entry, value; entry.append(tr("Batch Size")); - value.append(QString("%1").arg(Measurement::displayAmount(Measurement::Amount{rec->finalVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::finalVolume_l))); + value.append(QString("%1").arg(Measurement::displayAmount(Measurement::Amount{rec->finalVolume_l(), + Measurement::Units::liters}))); entry.append(tr("Boil Size")); - value.append(QString("%1").arg(Measurement::displayAmount(Measurement::Amount{rec->boilVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilVolume_l))); + value.append(QString("%1").arg(Measurement::displayAmount(Measurement::Amount{rec->boilVolume_l(), + Measurement::Units::liters}))); entry.append(tr("Boil Time")); value.append( QString("%1").arg( Measurement::displayAmount(Measurement::Amount{rec->equipment() == nullptr ? 0.0 : rec->equipment()->boilTime_min(), - Measurement::Units::minutes}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::boilTime_min) + Measurement::Units::minutes}) ) ); entry.append(tr("Efficiency")); value.append(QString("%1%").arg(rec->efficiency_pct(), 0, 'f', 0)); entry.append(tr("OG")); - value.append(QString("%1").arg(Measurement::displayAmount(Measurement::Amount{rec->og(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::og, - 3))); + value.append(QString("%1").arg(Measurement::displayAmount(Measurement::Amount{rec->og(), + Measurement::Units::specificGravity}, 3))); entry.append(tr("FG")); - value.append(QString("%1").arg(Measurement::displayAmount(Measurement::Amount{rec->fg(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::fg, - 3))); + value.append(QString("%1").arg(Measurement::displayAmount(Measurement::Amount{rec->fg(), + Measurement::Units::specificGravity}, 3))); entry.append(tr("ABV")); value.append(QString("%1%").arg(Measurement::displayQuantity(rec->ABV_pct(), 1))); entry.append(tr("Bitterness")); @@ -419,10 +396,8 @@ class RecipeFormatter::impl { .arg(tr("IBU")) .arg(IbuMethods::ibuFormulaName())); entry.append(tr("Color")); - value.append(QString("%1 (%2)").arg(Measurement::displayAmount(Measurement::Amount{rec->color_srm(), Measurement::Units::srm}, - PersistentSettings::Sections::tab_recipe, - PropertyNames::Recipe::color_srm, - 1)) + value.append(QString("%1 (%2)").arg(Measurement::displayAmount(Measurement::Amount{rec->color_srm(), + Measurement::Units::srm}, 1)) .arg(ColorMethods::colorFormulaName())); padAllToMaxLength(&entry); @@ -474,24 +449,17 @@ class RecipeFormatter::impl { ftable += QString("%1%2%3%4%5%6%%7") .arg(ferm->name()) .arg(Fermentable::typeDisplayNames[ferm->type()]) - .arg(Measurement::displayAmount(ferm->amountWithUnits(), - PersistentSettings::Sections::fermentableTable, - PropertyNames::Fermentable::amountWithUnits)) + .arg(Measurement::displayAmount(ferm->amountWithUnits())) .arg(ferm->isMashed() ? tr("Yes") : tr("No") ) .arg(ferm->addAfterBoil() ? tr("Yes") : tr("No")) .arg(Measurement::displayQuantity(ferm->yield_pct(), 0) ) - .arg(Measurement::displayAmount(Measurement::Amount{ferm->color_srm(), Measurement::Units::srm}, - PersistentSettings::Sections::fermentableTable, - PropertyNames::Fermentable::color_srm, - 1)); + .arg(Measurement::displayAmount(Measurement::Amount{ferm->color_srm(), Measurement::Units::srm}, 1)); } // One row for the total grain (QTextBrowser does not know the caption tag) ftable += QString("%1%2%3%4%5%6%7") .arg(tr("Total")) .arg("—" ) - .arg(Measurement::displayAmount(Measurement::Amount{rec->grains_kg(), Measurement::Units::kilograms}, - PersistentSettings::Sections::fermentableTable, - PropertyNames::Fermentable::amount)) + .arg(Measurement::displayAmount(Measurement::Amount{rec->grains_kg(), Measurement::Units::kilograms})) .arg("—") .arg("—") .arg("—") @@ -523,16 +491,12 @@ class RecipeFormatter::impl { Fermentable* ferm = ferms[ii]; names.append(ferm->name()); types.append(Fermentable::typeDisplayNames[ferm->type()]); - amounts.append(Measurement::displayAmount(ferm->amountWithUnits(), - PersistentSettings::Sections::fermentableTable, - PropertyNames::Fermentable::amountWithUnits)); + amounts.append(Measurement::displayAmount(ferm->amountWithUnits())); masheds.append( ferm->isMashed() ? tr("Yes") : tr("No")); lates.append( ferm->addAfterBoil() ? tr("Yes") : tr("No")); yields.append( QString("%1%").arg(Measurement::displayQuantity(ferm->yield_pct(), 0) ) ); - colors.append( QString("%1").arg(Measurement::displayAmount(Measurement::Amount{ferm->color_srm(), Measurement::Units::srm}, - PersistentSettings::Sections::fermentableTable, - PropertyNames::Fermentable::color_srm, - 1))); + colors.append( QString("%1").arg(Measurement::displayAmount(Measurement::Amount{ferm->color_srm(), + Measurement::Units::srm}, 1))); } padAllToMaxLength(&names); @@ -548,9 +512,7 @@ class RecipeFormatter::impl { } ret += QString("%1 %2\n").arg(tr("Total grain:")).arg( - Measurement::displayAmount(Measurement::Amount{rec->grains_kg(), Measurement::Units::kilograms}, - PersistentSettings::Sections::fermentableTable, - PropertyNames::Fermentable::amount) + Measurement::displayAmount(Measurement::Amount{rec->grains_kg(), Measurement::Units::kilograms}) ); } return ret; @@ -593,13 +555,9 @@ class RecipeFormatter::impl { hTable += QString("%1%2%%3%4%5%6%7") .arg(hop->name()) .arg(Measurement::displayQuantity(hop->alpha_pct(), 1) ) - .arg(Measurement::displayAmount(Measurement::Amount{hop->amount_kg(), Measurement::Units::kilograms}, - PersistentSettings::Sections::hopTable, - PropertyNames::Hop::amount_kg)) + .arg(Measurement::displayAmount(Measurement::Amount{hop->amount_kg(), Measurement::Units::kilograms})) .arg(Hop::useDisplayNames[hop->use()]) - .arg(Measurement::displayAmount(Measurement::Amount{hop->time_min(), Measurement::Units::minutes}, - PersistentSettings::Sections::hopTable, - PropertyNames::Hop::time_min)) + .arg(Measurement::displayAmount(Measurement::Amount{hop->time_min(), Measurement::Units::minutes})) .arg(Hop::formDisplayNames[hop->form()]) .arg(Measurement::displayQuantity(rec->ibuFromHop(hop), 1) ); } @@ -631,13 +589,10 @@ class RecipeFormatter::impl { names.append(hop->name()); alphas.append(QString("%1%").arg(Measurement::displayQuantity(hop->alpha_pct(), 1))); - amounts.append(Measurement::displayAmount(Measurement::Amount{hop->amount_kg(), Measurement::Units::kilograms}, - PersistentSettings::Sections::hopTable, - PropertyNames::Hop::amount_kg)); + amounts.append(Measurement::displayAmount(Measurement::Amount{hop->amount_kg(), + Measurement::Units::kilograms})); uses.append(Hop::useDisplayNames[hop->use()]); - times.append(Measurement::displayAmount(Measurement::Amount{hop->time_min(), Measurement::Units::minutes}, - PersistentSettings::Sections::hopTable, - PropertyNames::Hop::time_min)); + times.append(Measurement::displayAmount(Measurement::Amount{hop->time_min(), Measurement::Units::minutes})); forms.append(Hop::formDisplayNames[hop->form()]); ibus.append(QString("%1").arg( Measurement::displayQuantity(rec->ibuFromHop(hop), 1))); } @@ -687,19 +642,11 @@ class RecipeFormatter::impl { Misc *misc = miscs[ii]; mtable += QString("%1%2%3%4%5") - .arg( misc->name()) - .arg( misc->typeStringTr()) - .arg( misc->useStringTr()) - .arg( Measurement::displayAmount(Measurement::Amount{ - misc->amount(), - misc->amountIsWeight() ? Measurement::Units::kilograms : Measurement::Units::liters - }, - PersistentSettings::Sections::miscTableModel, - PropertyNames::Misc::amount, - 3)) - .arg( Measurement::displayAmount(Measurement::Amount{misc->time(), Measurement::Units::minutes}, - PersistentSettings::Sections::miscTableModel, - PropertyNames::Misc::time)); + .arg(misc->name()) + .arg(Misc::typeDisplayNames[misc->type()]) + .arg(Misc::useDisplayNames[misc->use()]) + .arg(Measurement::displayAmount(misc->amountWithUnits())) + .arg(Measurement::displayAmount(Measurement::Amount{misc->time_min(), Measurement::Units::minutes})); } mtable += ""; return mtable; @@ -726,20 +673,10 @@ class RecipeFormatter::impl { for (int ii = 0; ii < size; ++ii) { Misc* misc = miscs[ii]; names.append(misc->name()); - types.append(misc->typeStringTr()); - uses.append(misc->useStringTr()); - amounts.append( - Measurement::displayAmount(Measurement::Amount{ - misc->amount(), - misc->amountIsWeight() ? Measurement::Units::kilograms : Measurement::Units::liters - }, - PersistentSettings::Sections::miscTableModel, - PropertyNames::Misc::amount, - 3) - ); - times.append(Measurement::displayAmount(Measurement::Amount{misc->time(), Measurement::Units::minutes}, - PersistentSettings::Sections::miscTableModel, - PropertyNames::Misc::time)); + types.append(Misc::typeDisplayNames[misc->type()]); + uses.append(Misc::useDisplayNames[misc->use()]); + amounts.append(Measurement::displayAmount(misc->amountWithUnits())); + times.append(Measurement::displayAmount(Measurement::Amount{misc->time_min(), Measurement::Units::minutes})); } padAllToMaxLength(&names); @@ -792,8 +729,6 @@ class RecipeFormatter::impl { y->amount(), y->amountIsWeight() ? Measurement::Units::kilograms : Measurement::Units::liters }, - PersistentSettings::Sections::yeastTableModel, - PropertyNames::Yeast::amount, 2) ) .arg( y->addToSecondary() ? tr("Secondary") : tr("Primary")); } @@ -827,8 +762,6 @@ class RecipeFormatter::impl { y->amount(), y->amountIsWeight() ? Measurement::Units::kilograms : Measurement::Units::liters }, - PersistentSettings::Sections::yeastTableModel, - PropertyNames::Yeast::amount, 2)); stages.append(y->addToSecondary() ? tr("Secondary") : tr("Primary")); } @@ -881,28 +814,22 @@ class RecipeFormatter::impl { .arg(step->typeStringTr()); if (step->isInfusion()) { - tmp = tmp.arg(Measurement::displayAmount(Measurement::Amount{step->infuseAmount_l(), Measurement::Units::liters}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::infuseAmount_l)) - .arg(Measurement::displayAmount(Measurement::Amount{step->infuseTemp_c(), Measurement::Units::celsius}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::infuseTemp_c)); + tmp = tmp.arg(Measurement::displayAmount(Measurement::Amount{step->infuseAmount_l(), + Measurement::Units::liters})) + .arg(Measurement::displayAmount(Measurement::Amount{step->infuseTemp_c(), + Measurement::Units::celsius})); } else if (step->isDecoction()) { - tmp = tmp.arg( Measurement::displayAmount(Measurement::Amount{step->decoctionAmount_l(), Measurement::Units::liters}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::decoctionAmount_l)) + tmp = tmp.arg( Measurement::displayAmount(Measurement::Amount{step->decoctionAmount_l(), + Measurement::Units::liters})) .arg("---"); } else { tmp = tmp.arg( "---" ).arg("---"); } - tmp = tmp.arg( Measurement::displayAmount(Measurement::Amount{step->stepTemp_c(), Measurement::Units::celsius}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::stepTemp_c) ); - tmp = tmp.arg( Measurement::displayAmount(Measurement::Amount{step->stepTime_min(), Measurement::Units::minutes}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::Misc::time, - 0) ); + tmp = tmp.arg( Measurement::displayAmount(Measurement::Amount{step->stepTemp_c(), + Measurement::Units::celsius})); + tmp = tmp.arg( Measurement::displayAmount(Measurement::Amount{step->stepTime_min(), + Measurement::Units::minutes}, 0)); mtable += tmp + ""; } @@ -936,28 +863,22 @@ class RecipeFormatter::impl { names.append(step->name()); types.append(step->typeStringTr()); if ( step->isInfusion() ) { - amounts.append(Measurement::displayAmount(Measurement::Amount{step->infuseAmount_l(), Measurement::Units::liters}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::infuseAmount_l)); - temps.append(Measurement::displayAmount(Measurement::Amount{step->infuseTemp_c(), Measurement::Units::celsius}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::infuseTemp_c)); + amounts.append(Measurement::displayAmount(Measurement::Amount{step->infuseAmount_l(), + Measurement::Units::liters})); + temps.append(Measurement::displayAmount(Measurement::Amount{step->infuseTemp_c(), + Measurement::Units::celsius})); } else if( step->isDecoction() ) { - amounts.append(Measurement::displayAmount(Measurement::Amount{step->decoctionAmount_l(), Measurement::Units::liters}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::decoctionAmount_l)); + amounts.append(Measurement::displayAmount(Measurement::Amount{step->decoctionAmount_l(), + Measurement::Units::liters})); temps.append("---"); } else { amounts.append( "---" ); temps.append("---"); } - targets.append(Measurement::displayAmount(Measurement::Amount{step->stepTemp_c(), Measurement::Units::celsius}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::MashStep::stepTemp_c)); - times.append(Measurement::displayAmount(Measurement::Amount{step->stepTime_min(), Measurement::Units::minutes}, - PersistentSettings::Sections::mashStepTableModel, - PropertyNames::Misc::time, - 0)); + targets.append(Measurement::displayAmount(Measurement::Amount{step->stepTemp_c(), + Measurement::Units::celsius})); + times.append(Measurement::displayAmount(Measurement::Amount{step->stepTime_min(), + Measurement::Units::minutes}, 0)); } padAllToMaxLength(&names); @@ -1073,33 +994,25 @@ class RecipeFormatter::impl { bnTable += QString("%1").arg(tr("Preboil")); bnTable += QString("%1%2%3%4") .arg(tr("SG")) - .arg(Measurement::displayAmount(Measurement::Amount{note->sg(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::page_preboil, - PropertyNames::BrewNote::sg, - 3)) + .arg(Measurement::displayAmount(Measurement::Amount{note->sg(), Measurement::Units::specificGravity}, 3)) .arg(tr("Volume into BK")) - .arg(Measurement::displayAmount(Measurement::Amount{note->volumeIntoBK_l(), Measurement::Units::liters}, - PersistentSettings::Sections::page_preboil, - PropertyNames::BrewNote::volumeIntoBK_l)); + .arg(Measurement::displayAmount(Measurement::Amount{note->volumeIntoBK_l(), + Measurement::Units::liters})); bnTable += QString("%1%2%3%4") .arg(tr("Strike Temp")) - .arg(Measurement::displayAmount(Measurement::Amount{note->strikeTemp_c(), Measurement::Units::celsius}, - PersistentSettings::Sections::page_preboil, - PropertyNames::BrewNote::strikeTemp_c)) + .arg(Measurement::displayAmount(Measurement::Amount{note->strikeTemp_c(), + Measurement::Units::celsius})) .arg(tr("Final Temp")) - .arg(Measurement::displayAmount(Measurement::Amount{note->mashFinTemp_c(), Measurement::Units::celsius}, - PersistentSettings::Sections::page_preboil, - PropertyNames::BrewNote::mashFinTemp_c)); + .arg(Measurement::displayAmount(Measurement::Amount{note->mashFinTemp_c(), + Measurement::Units::celsius})); bnTable += QString("%1%2%%3%4") .arg(tr("Eff into BK")) .arg(Measurement::displayQuantity(note->calculateEffIntoBK_pct(), 2)) .arg(tr("Projected OG")) - .arg(Measurement::displayAmount(Measurement::Amount{note->calculateOg(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::page_preboil, - PropertyNames::BrewNote::projOg, - 3)); + .arg(Measurement::displayAmount(Measurement::Amount{note->calculateOg(), + Measurement::Units::specificGravity}, 3)); bnTable += ""; // POSTBOIL @@ -1107,19 +1020,14 @@ class RecipeFormatter::impl { bnTable += QString("%1").arg(tr("Postboil")); bnTable += QString("%1%2%3%4") .arg(tr("OG")) - .arg(Measurement::displayAmount(Measurement::Amount{note->og(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::page_postboil, - PropertyNames::BrewNote::og, - 3)) + .arg(Measurement::displayAmount(Measurement::Amount{note->og(), Measurement::Units::specificGravity}, 3)) .arg(tr("Postboil Volume")) - .arg(Measurement::displayAmount(Measurement::Amount{note->postBoilVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::page_postboil, - PropertyNames::BrewNote::postBoilVolume_l)); + .arg(Measurement::displayAmount(Measurement::Amount{note->postBoilVolume_l(), + Measurement::Units::liters})); bnTable += QString("%1%2%3%4") .arg(tr("Volume Into Fermenter")) - .arg(Measurement::displayAmount(Measurement::Amount{note->volumeIntoFerm_l(), Measurement::Units::liters}, - PersistentSettings::Sections::page_postboil, - PropertyNames::BrewNote::volumeIntoFerm_l)) + .arg(Measurement::displayAmount(Measurement::Amount{note->volumeIntoFerm_l(), + Measurement::Units::liters})) .arg(tr("Brewhouse Eff")) .arg(Measurement::displayQuantity(note->calculateBrewHouseEff_pct(), 2)); bnTable += QString("%1%2%") @@ -1133,14 +1041,9 @@ class RecipeFormatter::impl { bnTable += QString("%1").arg(tr("Postferment")); bnTable += QString("%1%2%3%4") .arg(tr("FG")) - .arg(Measurement::displayAmount(Measurement::Amount{note->fg(), Measurement::Units::sp_grav}, - PersistentSettings::Sections::page_postferment, - PropertyNames::BrewNote::fg, - 3)) + .arg(Measurement::displayAmount(Measurement::Amount{note->fg(), Measurement::Units::specificGravity}, 3)) .arg(tr("Volume")) - .arg(Measurement::displayAmount(Measurement::Amount{note->finalVolume_l(), Measurement::Units::liters}, - PersistentSettings::Sections::page_postferment, - PropertyNames::BrewNote::finalVolume_l)); + .arg(Measurement::displayAmount(Measurement::Amount{note->finalVolume_l(), Measurement::Units::liters})); bnTable += QString("%1%2%3%4") .arg(tr("Date")) @@ -1314,10 +1217,10 @@ QString RecipeFormatter::getToolTip(Recipe* rec) { // Third row: OG and FG body += QString("%1%2") .arg(tr("OG")) - .arg(Measurement::displayAmount(Measurement::Amount{rec->og(), Measurement::Units::sp_grav}, 3)); + .arg(Measurement::displayAmount(Measurement::Amount{rec->og(), Measurement::Units::specificGravity}, 3)); body += QString("%1%2") .arg(tr("FG")) - .arg(Measurement::displayAmount(Measurement::Amount{rec->fg(), Measurement::Units::sp_grav}, 3)); + .arg(Measurement::displayAmount(Measurement::Amount{rec->fg(), Measurement::Units::specificGravity}, 3)); // Fourth row: Color and Bitterness. body += QString("%1%2 (%3)") @@ -1495,14 +1398,13 @@ QString RecipeFormatter::getToolTip(Misc* misc) { body += QString(""); body += QString("") .arg( misc->name() ); - // First row -- type and use body += QString("") .arg(tr("Type")) - .arg(misc->typeStringTr()); + .arg(Misc::typeDisplayNames[misc->type()]); body += QString("") .arg(tr("Use")) - .arg(misc->useStringTr()); + .arg(Misc::useDisplayNames[misc->use()]); body += "
%1
%1%2%1%2
"; diff --git a/src/RefractoDialog.cpp b/src/RefractoDialog.cpp index 9b435673..f2fe4709 100644 --- a/src/RefractoDialog.cpp +++ b/src/RefractoDialog.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * RefractoDialog.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * RefractoDialog.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Brian Rower * • Eric Tamme * • Matt Young @@ -32,7 +32,18 @@ RefractoDialog::RefractoDialog(QWidget* parent) : QDialog(parent) { setupUi(this); - connect( pushButton_calculate, &QAbstractButton::clicked, this, &RefractoDialog::calculate ); + // Note that the labels here are QLabel, not SmartLabel, because we want the units fixed, not user-selectable + SMART_FIELD_INIT_FIXED(RefractoDialog, label_op , lineEdit_op , double, Measurement::Units::plato , 1); // Original Plato + SMART_FIELD_INIT_FIXED(RefractoDialog, label_inputOG, lineEdit_inputOG, double, Measurement::Units::specificGravity, 3); // Original gravity in + SMART_FIELD_INIT_FIXED(RefractoDialog, label_cp , lineEdit_cp , double, Measurement::Units::plato , 1); // Current Plato + SMART_FIELD_INIT_FIXED(RefractoDialog, label_og , lineEdit_og , double, Measurement::Units::specificGravity, 3); // Original gravity out + SMART_FIELD_INIT_FIXED(RefractoDialog, label_sg , lineEdit_sg , double, Measurement::Units::specificGravity, 3); // Specific gravity out + SMART_FIELD_INIT_FIXED(RefractoDialog, label_re , lineEdit_re , double, Measurement::Units::plato , 1); // Real extract Plato + SMART_FIELD_INIT_FS (RefractoDialog, label_ri , lineEdit_ri , double, NonPhysicalQuantity::Dimensionless ); // Refractive index + SMART_FIELD_INIT_FS (RefractoDialog, label_abv , lineEdit_abv , double, NonPhysicalQuantity::Percentage ); // Alcohol by volume + SMART_FIELD_INIT_FS (RefractoDialog, label_abw , lineEdit_abw , double, NonPhysicalQuantity::Percentage ); // Alcohol by weight + + connect(this->pushButton_calculate, &QAbstractButton::clicked, this, &RefractoDialog::calculate ); return; } @@ -45,11 +56,11 @@ void RefractoDialog::calculate() { // User can enter in specific gravity or Plato, but the lineEdit is going to convert it to Plato, so we can just // grab the number - double originalPlato = lineEdit_op->toDoubleRaw(&haveOP); - double inputOG = lineEdit_inputOG->toDoubleRaw(&haveOG); - double currentPlato = lineEdit_cp->toDoubleRaw(&haveCP); + double originalPlato = Measurement::extractRawFromString(lineEdit_op ->text(), &haveOP); + double inputOG = Measurement::extractRawFromString(lineEdit_inputOG->text(), &haveOG); + double currentPlato = Measurement::extractRawFromString(lineEdit_cp ->text(), &haveCP); - clearOutputFields(); + this->clearOutputFields(); // Abort if we don't have the current plato. // I really dislike just doing nothing as the user is POUNDING on the @@ -60,14 +71,14 @@ void RefractoDialog::calculate() { } double ri = Algorithms::refractiveIndex(currentPlato); - lineEdit_ri->setText(Measurement::displayQuantity(ri, 3)); + this->lineEdit_ri->setText(Measurement::displayQuantity(ri, 3)); if (!haveOG && haveOP) { - inputOG = Algorithms::PlatoToSG_20C20C( originalPlato ); - lineEdit_inputOG->setText(inputOG); + inputOG = Algorithms::PlatoToSG_20C20C(originalPlato); + this->lineEdit_inputOG->setAmount(inputOG); } else if (!haveOP && haveOG) { - originalPlato = Algorithms::SG_20C20C_toPlato( inputOG ); - lineEdit_op->setText(inputOG); + originalPlato = Algorithms::SG_20C20C_toPlato(inputOG); + this->lineEdit_op->setAmount(inputOG); } else if (!haveOP && !haveOG) { qDebug() << Q_FUNC_INFO << "no plato or og"; return; // Can't do much if we don't have OG or OP. @@ -81,9 +92,9 @@ void RefractoDialog::calculate() { sg = og; } - double re = Algorithms::realExtract( sg, currentPlato ); - double abv = Algorithms::getABVBySGPlato( sg, currentPlato ); - double abw = Algorithms::getABWBySGPlato( sg, currentPlato ); + double re = Algorithms::realExtract (sg, currentPlato); + double abv = Algorithms::getABVBySGPlato(sg, currentPlato); + double abw = Algorithms::getABWBySGPlato(sg, currentPlato); // Warn the user if the inputOG and calculated og don't match. if( qAbs(og - inputOG) > 0.002 ) { @@ -95,22 +106,22 @@ void RefractoDialog::calculate() { ); } - lineEdit_og->setText(og); - lineEdit_sg->setText(sg); - //Even if the real extract if display in Plato, it must be given in system unit. - //Conversion is made by BtLineEdit - lineEdit_re->setText(Algorithms::PlatoToSG_20C20C(re)); - lineEdit_abv->setText(abv); - lineEdit_abw->setText(abw); + this->lineEdit_og->setAmount(og); + this->lineEdit_sg->setAmount(sg); + // Even if the real extract if display in Plato, it must be given in system unit. + // Conversion is made by SmartLineEdit + this->lineEdit_re ->setAmount(Algorithms::PlatoToSG_20C20C(re)); + this->lineEdit_abv->setAmount(abv); + this->lineEdit_abw->setAmount(abw); return; } void RefractoDialog::clearOutputFields() { - lineEdit_ri->clear(); - lineEdit_og->clear(); - lineEdit_sg->clear(); - lineEdit_re->clear(); - lineEdit_abv->clear(); - lineEdit_abw->clear(); + this->lineEdit_ri ->clear(); + this->lineEdit_og ->clear(); + this->lineEdit_sg ->clear(); + this->lineEdit_re ->clear(); + this->lineEdit_abv->clear(); + this->lineEdit_abw->clear(); return; } diff --git a/src/SimpleUndoableUpdate.h b/src/SimpleUndoableUpdate.h deleted file mode 100644 index ec4ad11c..00000000 --- a/src/SimpleUndoableUpdate.h +++ /dev/null @@ -1,76 +0,0 @@ -/*====================================================================================================================== - * SimpleUndoableUpdate.h is part of Brewken, and is copyright the following authors 2020-2021: - * • Matt Young - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#ifndef SIMPLE_UNDOABLE_UPDATE_H -#define SIMPLE_UNDOABLE_UPDATE_H -#pragma once - -#include -#include -#include -#include -#include - -#include "utils/BtStringConst.h" - -/*! - * \class SimpleUndoableUpdate - * - * \brief Each instance of this class is an undoable update to a 'simple' editable field of a recipe, style, etc. - * By simple, we mean that there is one of them and that it is non-relational (ie can be passed and set by value). - * The thing being updated needs to inherit from Q_OBJECT and the field being changed needs to have been - * declared as a Q_PROPERTY. - */ -class SimpleUndoableUpdate : public QUndoCommand { -public: - /*! - * \param updatee The entity (eg recipe) we are updating - * \param propertyName Which property we are updating - needs to have been declared as a Q_PROPERTY in the class header file - * \param newValue The new value to assign - * \param description Short text we can show on undo/redo menu to describe this update eg "Change Recipe Name" - * \param parent This is for grouping updates together. We don't currently use it. - */ - SimpleUndoableUpdate(QObject & updatee, - BtStringConst const & propertyName, - QVariant newValue, - QString const & description, - QUndoCommand * parent = nullptr); - - ~SimpleUndoableUpdate(); - - /*! - * \brief Apply the update (including for the first time) - */ - void redo(); - - /*! - * \brief Undo applying the update - */ - void undo(); - -private: - /*! - * \brief Undo or redo applying the update - * \param isUndo true for undo, false for redo - * \return true if succeeded, false otherwise - not currently used but potentially useful for a derived class - */ - bool undoOrRedo(bool const isUndo); - - QObject & updatee; - BtStringConst const propertyName; - QVariant oldValue, newValue; -}; - -#endif diff --git a/src/StrikeWaterDialog.cpp b/src/StrikeWaterDialog.cpp index a5c92631..c16b20ce 100644 --- a/src/StrikeWaterDialog.cpp +++ b/src/StrikeWaterDialog.cpp @@ -56,6 +56,20 @@ namespace { StrikeWaterDialog::StrikeWaterDialog(QWidget* parent) : QDialog(parent) { setupUi(this); + + // .:TBD:. These label and lineEdit fields could be slightly better named... + SMART_FIELD_INIT_FS(StrikeWaterDialog, grainTempLbl , grainTempVal , double, Measurement::PhysicalQuantity::Temperature); // Initial Infusion: Original Grain Temperature + SMART_FIELD_INIT_FS(StrikeWaterDialog, targetMashLbl , targetMashVal , double, Measurement::PhysicalQuantity::Temperature); // Initial Infusion: Target Mash Temperature + SMART_FIELD_INIT_FS(StrikeWaterDialog, grainWeightInitLbl, grainWeightInitVal, double, Measurement::PhysicalQuantity::Mass ); // Initial Infusion: Weight of Grain + SMART_FIELD_INIT_FS(StrikeWaterDialog, waterVolumeLbl , waterVolumeVal , double, Measurement::PhysicalQuantity::Volume ); // Initial Infusion: Volume of Water + SMART_FIELD_INIT_FS(StrikeWaterDialog, mashVolLbl , mashVolVal , double, Measurement::PhysicalQuantity::Volume ); // Mash Infusion: Total Volume of Water + SMART_FIELD_INIT_FS(StrikeWaterDialog, grainWeightLbl , grainWeightVal , double, Measurement::PhysicalQuantity::Mass ); // Mash Infusion: Grain Weight + SMART_FIELD_INIT_FS(StrikeWaterDialog, actualMashLbl , actualMashVal , double, Measurement::PhysicalQuantity::Temperature); // Mash Infusion: Actual Mash Temperature + SMART_FIELD_INIT_FS(StrikeWaterDialog, targetMashInfLbl , targetMashInfVal , double, Measurement::PhysicalQuantity::Temperature); // Mash Infusion: Target Mash Temperature + SMART_FIELD_INIT_FS(StrikeWaterDialog, infusionWaterLbl , infusionWaterVal , double, Measurement::PhysicalQuantity::Temperature); // Mash Infusion: Infusion Water Temperature + SMART_FIELD_INIT_FS(StrikeWaterDialog, initialResultLbl , initialResultTxt , double, Measurement::PhysicalQuantity::Temperature); // Result: Strike Water Temperature + SMART_FIELD_INIT_FS(StrikeWaterDialog, mashResultLbl , mashResultTxt , double, Measurement::PhysicalQuantity::Volume ); // Result: Volume to add + connect(pushButton_calculate, &QAbstractButton::clicked, this, &StrikeWaterDialog::calculate); return; } @@ -63,18 +77,18 @@ StrikeWaterDialog::StrikeWaterDialog(QWidget* parent) : QDialog(parent) { StrikeWaterDialog::~StrikeWaterDialog() = default; void StrikeWaterDialog::calculate() { - double initial = computeInitialInfusion(); - double mash = computeMashInfusion(); + double strikeWaterTemp = computeInitialInfusion(); + double volumeToAdd = computeMashInfusion(); - this->initialResultTxt->setText(initial); - this->mashResultTxt->setText(mash); + this->initialResultTxt->setAmount(strikeWaterTemp); + this->mashResultTxt ->setAmount(volumeToAdd); return; } double StrikeWaterDialog::computeInitialInfusion() { - double grainTemp = this->grainTempVal->toCanonical().quantity(); - double targetMash = this->targetMashVal->toCanonical().quantity(); - double waterVolume = this->waterVolumeVal->toCanonical().quantity(); + double grainTemp = this->grainTempVal ->toCanonical().quantity(); + double targetMash = this->targetMashVal ->toCanonical().quantity(); + double waterVolume = this->waterVolumeVal ->toCanonical().quantity(); double grainWeight = this->grainWeightInitVal->toCanonical().quantity(); if (grainWeight == 0.0) { @@ -85,9 +99,9 @@ double StrikeWaterDialog::computeInitialInfusion() { } double StrikeWaterDialog::computeMashInfusion() { - double mashVol = this->mashVolVal->toCanonical().quantity(); - double grainWeight = this->grainWeightVal->toCanonical().quantity(); - double actualMash = this->actualMashVal->toCanonical().quantity(); + double mashVol = this->mashVolVal ->toCanonical().quantity(); + double grainWeight = this->grainWeightVal ->toCanonical().quantity(); + double actualMash = this->actualMashVal ->toCanonical().quantity(); double targetMashInf = this->targetMashInfVal->toCanonical().quantity(); double infusionWater = this->infusionWaterVal->toCanonical().quantity(); diff --git a/src/UiAmountWithUnits.cpp b/src/UiAmountWithUnits.cpp deleted file mode 100644 index 6e7287b6..00000000 --- a/src/UiAmountWithUnits.cpp +++ /dev/null @@ -1,230 +0,0 @@ -/*====================================================================================================================== - * UiAmountWithUnits.cpp is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Mark de Wever - * • Mattias Måhl - * • Matt Young - * • Mike Evans - * • Mik Firestone - * • Philip Greggory Lee - * • Théophane Martin - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "UiAmountWithUnits.h" - -#include - -#include -#include -#include - -#include "Localization.h" -#include "measurement/Measurement.h" -#include "utils/OptionalHelpers.h" - -UiAmountWithUnits::UiAmountWithUnits(QWidget * parent, - Measurement::PhysicalQuantities const physicalQuantities, - Measurement::Unit const * units) : - parent{parent}, - physicalQuantities{physicalQuantities}, - units{units} { - return; -} - -UiAmountWithUnits::~UiAmountWithUnits() = default; - -Measurement::PhysicalQuantity UiAmountWithUnits::getPhysicalQuantity() const { - // If it's not a "mixed" field (ie one in which the measurement could be for more than one - // Measurement::PhysicalQuantity, eg Mass & Volume), just return what it is - if (std::holds_alternative(this->physicalQuantities)) { - return std::get(this->physicalQuantities); - } - // If it is a "mixed" field, we should return the physical quantity corresponding to the current units - auto const physicalQuantity = this->units->getPhysicalQuantity(); - // It's a coding error if we somehow have units other than Mass or Volume - auto const & tupleOfPqs = std::get(this->physicalQuantities); - Q_ASSERT(std::get<0>(tupleOfPqs) == physicalQuantity || - std::get<1>(tupleOfPqs) == physicalQuantity); - return physicalQuantity; -} - -void UiAmountWithUnits::setForcedSystemOfMeasurement(std::optional systemOfMeasurement) { - Measurement::setForcedSystemOfMeasurementForField(this->editField, this->configSection, systemOfMeasurement); - return; -} - -std::optional UiAmountWithUnits::getForcedSystemOfMeasurement() const { - return Measurement::getForcedSystemOfMeasurementForField(this->editField, this->configSection); -} - -void UiAmountWithUnits::setForcedSystemOfMeasurementViaString(QString systemOfMeasurementAsString) { - qDebug() << - Q_FUNC_INFO << "Measurement system" << systemOfMeasurementAsString << "for" << this->configSection << ">" << - this->editField; - this->setForcedSystemOfMeasurement(Measurement::getFromUniqueName(systemOfMeasurementAsString)); - return; -} - -QString UiAmountWithUnits::getForcedSystemOfMeasurementViaString() const { - auto forcedSystemOfMeasurement = this->getForcedSystemOfMeasurement(); - return forcedSystemOfMeasurement ? Measurement::getUniqueName(*forcedSystemOfMeasurement) : ""; -} - -void UiAmountWithUnits::setForcedRelativeScale(std::optional relativeScale) { - Measurement::setForcedRelativeScaleForField(this->editField, this->configSection, relativeScale); - return; -} - -std::optional UiAmountWithUnits::getForcedRelativeScale() const { - return Measurement::getForcedRelativeScaleForField(this->editField, this->configSection); -} - -void UiAmountWithUnits::setForcedRelativeScaleViaString(QString relativeScaleAsString) { - qDebug() << - Q_FUNC_INFO << "Scale" << relativeScaleAsString << "for" << this->configSection << ">" << this->editField; - this->setForcedRelativeScale(Measurement::UnitSystem::getScaleFromUniqueName(relativeScaleAsString)); - return; -} - -QString UiAmountWithUnits::getForcedRelativeScaleViaString() const { - auto forcedRelativeScale = this->getForcedRelativeScale(); - return forcedRelativeScale ? Measurement::UnitSystem::getUniqueName(*forcedRelativeScale) : ""; -} - -void UiAmountWithUnits::setEditField(QString editField) { - this->editField = editField; - return; -} - -QString UiAmountWithUnits::getEditField() const { - return this->editField; -} - -void UiAmountWithUnits::setConfigSection(QString configSection) { - // The cascade looks a little odd, but it is intentional. - this->configSection = configSection; - if (this->configSection.isEmpty()) { - this->configSection = this->parent->property("configSection").toString(); - } - if (this->configSection.isEmpty()) { - this->configSection = this->parent->objectName(); - } - return; -} - -QString UiAmountWithUnits::getConfigSection() { - if (this->configSection.isEmpty()) { - // Setting the config section to blank will actually attempt to populate it with default values -- see - // UiAmountWithUnits::setConfigSection() - this->setConfigSection(""); - } - - return this->configSection; -} - -double UiAmountWithUnits::toDoubleRaw(bool * ok) const { - return Measurement::extractRawFromString(this->getWidgetText(), ok); -} - -Measurement::Amount UiAmountWithUnits::toCanonical() const { - Q_ASSERT(this->units); - return Measurement::qStringToSI(this->getWidgetText(), - this->units->getPhysicalQuantity(), - this->getForcedSystemOfMeasurement(), - this->getForcedRelativeScale()); -} - - -QString UiAmountWithUnits::displayAmount(double amount, int precision) const { - // I find this a nice level of abstraction. This lets all of the setText() - // methods make a single call w/o having to do the logic for finding the - // unit and scale. - if (this->units) { - return Measurement::displayAmount(Measurement::Amount{amount, *this->units}, - precision, - this->getForcedSystemOfMeasurement(), - this->getForcedRelativeScale()); - } - return Measurement::displayQuantity(amount, precision); -} - -void UiAmountWithUnits::textOrUnitsChanged(PreviousScaleInfo previousScaleInfo) { - // This is where it gets hard - // - // We may need to fix the text that the user entered, eg if this field is set to show US Customary volumes and user - // enters an amount in litres then we need to convert it to display in pints or quarts etc. - QString correctedText; - - QString rawValue = this->getWidgetText(); - qDebug() << Q_FUNC_INFO << "rawValue:" << rawValue; - - if (rawValue.isEmpty()) { - return; - } - - // The idea here is we need to first translate the field into a known - // amount (aka to SI) and then into the unit we want. - Measurement::Amount amountAsCanonical = this->convertToSI(previousScaleInfo); - - Measurement::PhysicalQuantity physicalQuantity = this->getPhysicalQuantity(); - int precision = 3; - if (physicalQuantity == Measurement::PhysicalQuantity::Color) { - precision = 0; - } - correctedText = this->displayAmount(amountAsCanonical.quantity(), precision); - qDebug() << - Q_FUNC_INFO << "Interpreted" << rawValue << "as" << amountAsCanonical << "and corrected to" << correctedText; - qDebug() << Q_FUNC_INFO << "this->units=" << this->units; - - this->setWidgetText(correctedText); - return; -} - -Measurement::Amount UiAmountWithUnits::convertToSI(PreviousScaleInfo previousScaleInfo) { - QString rawValue = this->getWidgetText(); - qDebug() << - Q_FUNC_INFO << "rawValue:" << rawValue << ", old SystemOfMeasurement:" << previousScaleInfo.oldSystemOfMeasurement << - ", old ForcedScale: " << previousScaleInfo.oldForcedScale; - - Measurement::PhysicalQuantity physicalQuantity = this->getPhysicalQuantity(); - - Measurement::UnitSystem const & oldUnitSystem = - Measurement::UnitSystem::getInstance(previousScaleInfo.oldSystemOfMeasurement, physicalQuantity); - - Measurement::Unit const * defaultUnit{ - previousScaleInfo.oldForcedScale ? oldUnitSystem.scaleUnit(*previousScaleInfo.oldForcedScale) : oldUnitSystem.unit() - }; - - // It's a coding error if defaultUnit is null, because it means previousScaleInfo.oldForcedScale was not valid for - // oldUnitSystem. However, we can recover. - if (!defaultUnit) { - qWarning() << Q_FUNC_INFO << "previousScaleInfo.oldForcedScale invalid?" << previousScaleInfo.oldForcedScale; - defaultUnit = oldUnitSystem.unit(); - } - - // - // Normally, we display units with the text. If the user just edits the number, then the units will still be there. - // Alternatively, if the user specifies different units in the text, we should try to honour those. Otherwise, if, - // no units are specified in the text, we need to go to defaults. Defaults are either what is "forced" for this - // specific field or, failing that, what is configured globally. - // - // Measurement::UnitSystem::qStringToSI will handle all the logic to deal with any units specified by the user in the - // string. (In theory, we just grab the units that the user has specified in the input text. In reality, it's not - // that easy as we sometimes need to disambiguate - eg between Imperial gallons and US customary ones. So, if we - // have old or current units then that helps with this - eg, if current units are US customary cups and user enters - // gallons, then we'll go with US customary gallons over Imperial ones.) - // - auto amount = oldUnitSystem.qstringToSI(rawValue, *defaultUnit); - qDebug() << Q_FUNC_INFO << "Converted to" << amount; - return amount; -} diff --git a/src/UiAmountWithUnits.h b/src/UiAmountWithUnits.h deleted file mode 100644 index c6bcba1e..00000000 --- a/src/UiAmountWithUnits.h +++ /dev/null @@ -1,170 +0,0 @@ -/*====================================================================================================================== - * UiAmountWithUnits.h is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Mark de Wever - * • Matt Young - * • Mike Evans - * • Mik Firestone - * • Philip Greggory Lee - * • Scott Peshak - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#ifndef UIAMOUNTWITHUNITS_H -#define UIAMOUNTWITHUNITS_H -#pragma once - -#include - -#include - -#include "measurement/PhysicalQuantity.h" -#include "measurement/Unit.h" -#include "measurement/UnitSystem.h" - -class QWidget; - -struct PreviousScaleInfo { - Measurement::SystemOfMeasurement oldSystemOfMeasurement; - std::optional oldForcedScale = std::nullopt; -}; - -/** - * \class UiAmountWithUnits A base class, suitable for combining with \c QLabel, \c QLineEdit, etc, that handles all the - * unit transformation such a widget would need to do. It is inherited by \c BtDigitWidget and - * \c BtAmountEdit. - */ -class UiAmountWithUnits { -public: - /** - * \param parent - * \param physicalQuantities the \c PhysicalQuantity or \c Mixed2PhysicalQuantities to which this amount relates - * \param units - */ - UiAmountWithUnits(QWidget * parent, - Measurement::PhysicalQuantities const physicalQuantities, - Measurement::Unit const * units = nullptr); - virtual ~UiAmountWithUnits(); - - /** - * \brief A class inheriting from this class is also expected to also inherit from a \c QWidget such as \c QLabel or - * \c QLineEdit. We would like to be able to access the text() member function of that parent class in parts - * of our own implementation. This is a bit tricky as \c QLabel::text() and \c QLineEdit::text() are actually - * unrelated, despite both having the same signature. We therefore require child classes to implement this - * wrapper function that returns the value of \c text() from their other superclass. - */ - virtual QString getWidgetText() const = 0; - - /** - * \brief Similar to \c getText(), this allows this base class to access \c QLabel::setText() or - * \c QLineEdit::setText() in the subclass that also inherits from \c QLabel or \c QLineEdit. - */ - virtual void setWidgetText(QString text) = 0; - - /** - * \brief Returns what type of field this is - except that, if it is \c Mixed2PhysicalQuantities, will one of the two - * possible \c Measurement::PhysicalQuantity values depending on the value of \c this->units. - */ - Measurement::PhysicalQuantity getPhysicalQuantity() const; - - void setForcedSystemOfMeasurement(std::optional systemOfMeasurement); - void setForcedRelativeScale(std::optional relativeScale); - - std::optional getForcedSystemOfMeasurement() const; - std::optional getForcedRelativeScale() const; - - /** - * \brief QString version of \c setForcedSystemOfMeasurement to work with code generated from .ui files (via - * Q_PROPERTY declared in subclass of this class) - */ - void setForcedSystemOfMeasurementViaString(QString systemOfMeasurementAsString); - - /** - * \brief QString version of \c getForcedSystemOfMeasurement to work with code generated from .ui files (via - * Q_PROPERTY declared in subclass of this class) - */ - QString getForcedSystemOfMeasurementViaString() const; - - /** - * \brief QString version of \c setForcedRelativeScale to work with code generated from .ui files (via Q_PROPERTY - * declared in subclass of this class) - */ - void setForcedRelativeScaleViaString(QString relativeScaleAsString); - - /** - * \brief QString version of \c getForcedRelativeScale to work with code generated from .ui files (via Q_PROPERTY - * declared in subclass of this class) - */ - QString getForcedRelativeScaleViaString() const; - - // By defining the setters/getters, we can remove the need for initializeProperties. - void setEditField(QString editField); - QString getEditField() const; - - void setConfigSection(QString configSection); - QString getConfigSection(); - - /** - * \brief Converts the numeric part of the input field to a double, ignoring any string suffix. So "5.5 gal" will - * give 5.5, "20L" will return 20.0, and so on. - */ - double toDoubleRaw(bool * ok = nullptr) const; - - /** - * \brief Returns the field converted to canonical units for the relevant \c Measurement::PhysicalQuantity - */ - Measurement::Amount toCanonical() const; - - /** - * \brief Use this when you want to do something with the returned QString - */ - QString displayAmount(double amount, int precision = 3) const; - -protected: - /** - * \brief - */ - void textOrUnitsChanged(PreviousScaleInfo previousScaleInfo); - - /** - * \brief Returns the contents of the field converted, if necessary, to SI units - * - * \param oldSystemOfMeasurement - * \param oldScale (optional) - */ - Measurement::Amount convertToSI(PreviousScaleInfo previousScaleInfo); - -private: - QWidget * parent; - /** - * \brief Even inside the class (or any subclasses), this should never be accessed directly but always through - * \c this->getPhysicalQuantity, as there is special case handling for \c Mixed2PhysicalQuantities. - */ - Measurement::PhysicalQuantities const physicalQuantities; - -protected: - /** - * \brief If \c fieldType is a \c Measurement::PhysicalQuantity, this is the \c Measurement::Unit that should be used - * to store the amount of this field. This is normally fixed as our "standard" (normally metric) unit for the - * \c Measurement::PhysicalQuantity of the field -- eg kilograms for Mass, liters for Volume, - * celsius for Temperature, minutes for Time, etc. However, for \c fieldType of - * \c Mixed2PhysicalQuantities, this will need to vary between two different \c Measurement::Units values - * depending on which \c Measurement::PhysicalQuantity the field is currently set to measure. - * - * If \c fieldType is \c NonPhysicalQuantity, this will be \c nullptr - */ - Measurement::Unit const * units; - QString editField; - QString configSection; -}; - -#endif diff --git a/src/WaterDialog.cpp b/src/WaterDialog.cpp index b0d4d8be..7e0bc56a 100644 --- a/src/WaterDialog.cpp +++ b/src/WaterDialog.cpp @@ -36,10 +36,10 @@ #include "tableModels/SaltTableModel.h" #include "tableModels/WaterTableModel.h" #include "WaterButton.h" -#include "WaterEditor.h" +#include "editors/WaterEditor.h" #include "WaterListModel.h" #include "WaterSortFilterProxyModel.h" -#include "widgets/BtDigitWidget.h" +#include "widgets/SmartDigitWidget.h" // @@ -69,8 +69,8 @@ namespace { WaterDialog::WaterDialog(QWidget* parent) : QDialog{parent}, - m_ppm_digits{ QVector{static_cast(Water::Ions::numIons)} }, - m_total_digits{QVector{static_cast(Salt::Types::numTypes)} }, + m_ppm_digits{ QVector{static_cast(Water::Ions::numIons)} }, + m_total_digits{QVector{static_cast(Salt::Types::numTypes)} }, m_rec{nullptr}, m_base{nullptr}, m_target{nullptr}, @@ -78,9 +78,6 @@ WaterDialog::WaterDialog(QWidget* parent) : m_spargeRO{0.0}, m_total_grains{0.0}, m_thickness{0.0} { - QStringList msgs = QStringList() << tr("Too low for target profile.") - << tr("In range for target profile.") - << tr("Too high for target profile."); setupUi(this); // initialize the two buttons and lists (I think) @@ -100,36 +97,53 @@ WaterDialog::WaterDialog(QWidget* parent) : m_target_filter->sort(0); targetProfileCombo->setModel(m_target_filter); + SMART_FIELD_INIT_FS(WaterDialog, label_ca , btDigit_ca , double, Measurement::PhysicalQuantity::VolumeConcentration, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_cl , btDigit_cl , double, Measurement::PhysicalQuantity::VolumeConcentration, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_hco3, btDigit_hco3, double, Measurement::PhysicalQuantity::VolumeConcentration, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_mg , btDigit_mg , double, Measurement::PhysicalQuantity::VolumeConcentration, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_na , btDigit_na , double, Measurement::PhysicalQuantity::VolumeConcentration, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_so4 , btDigit_so4 , double, Measurement::PhysicalQuantity::VolumeConcentration, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_pH , btDigit_ph , double, Measurement::PhysicalQuantity::Acidity , 1); + + SMART_FIELD_INIT_FS(WaterDialog, label_totalcacl2 , btDigit_totalcacl2 , double, Measurement::PhysicalQuantity::Mass, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_totalcaco3 , btDigit_totalcaco3 , double, Measurement::PhysicalQuantity::Mass, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_totalcaso4 , btDigit_totalcaso4 , double, Measurement::PhysicalQuantity::Mass, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_totalmgso4 , btDigit_totalmgso4 , double, Measurement::PhysicalQuantity::Mass, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_totalnacl , btDigit_totalnacl , double, Measurement::PhysicalQuantity::Mass, 2); + SMART_FIELD_INIT_FS(WaterDialog, label_totalnahco3, btDigit_totalnahco3, double, Measurement::PhysicalQuantity::Mass, 2); + // not sure if this is better or worse, but we will try it out - m_ppm_digits[static_cast(Water::Ions::Ca)] = btDigit_ca; - m_ppm_digits[static_cast(Water::Ions::Cl)] = btDigit_cl; + m_ppm_digits[static_cast(Water::Ions::Ca)] = btDigit_ca ; + m_ppm_digits[static_cast(Water::Ions::Cl)] = btDigit_cl ; m_ppm_digits[static_cast(Water::Ions::HCO3)] = btDigit_hco3; - m_ppm_digits[static_cast(Water::Ions::Mg)] = btDigit_mg; - m_ppm_digits[static_cast(Water::Ions::Na)] = btDigit_na; - m_ppm_digits[static_cast(Water::Ions::SO4)] = btDigit_so4; - - m_total_digits[static_cast(Salt::Types::CACL2 )] = btDigit_totalcacl2; - m_total_digits[static_cast(Salt::Types::CACO3 )] = btDigit_totalcaco3; - m_total_digits[static_cast(Salt::Types::CASO4 )] = btDigit_totalcaso4; - m_total_digits[static_cast(Salt::Types::MGSO4 )] = btDigit_totalmgso4; - m_total_digits[static_cast(Salt::Types::NACL )] = btDigit_totalnacl; + m_ppm_digits[static_cast(Water::Ions::Mg)] = btDigit_mg ; + m_ppm_digits[static_cast(Water::Ions::Na)] = btDigit_na ; + m_ppm_digits[static_cast(Water::Ions::SO4)] = btDigit_so4 ; + + m_total_digits[static_cast(Salt::Types::CACL2 )] = btDigit_totalcacl2 ; + m_total_digits[static_cast(Salt::Types::CACO3 )] = btDigit_totalcaco3 ; + m_total_digits[static_cast(Salt::Types::CASO4 )] = btDigit_totalcaso4 ; + m_total_digits[static_cast(Salt::Types::MGSO4 )] = btDigit_totalmgso4 ; + m_total_digits[static_cast(Salt::Types::NACL )] = btDigit_totalnacl ; m_total_digits[static_cast(Salt::Types::NAHCO3)] = btDigit_totalnahco3; - // foreach( BtDigitWidget* i, m_ppm_digits ) { + // foreach( SmartDigitWidget* i, m_ppm_digits ) { for (int i = 0; i < static_cast(Water::Ions::numIons); ++i ) { m_ppm_digits[i]->setLimits(0.0,1000.0); - m_ppm_digits[i]->setText(0.0, 1); - m_ppm_digits[i]->setMessages(msgs); + m_ppm_digits[i]->setAmount(0.0); + m_ppm_digits[i]->setMessages(tr("Too low for target profile."), + tr("In range for target profile."), + tr("Too high for target profile.")); } // we can be a bit more specific with pH btDigit_ph->setLowLim(5.0); btDigit_ph->setHighLim(5.5); - btDigit_ph->display(7.0,1); + btDigit_ph->setAmount(7.0); // since all the things are now digits, lets get the totals configured for (int i = static_cast(Salt::Types::CACL2); i < static_cast(Salt::Types::NAHCO3); ++i ) { - m_total_digits[i]->setConstantColor(BtDigitWidget::BLACK); - m_total_digits[i]->setText(0.0,1); + m_total_digits[i]->setConstantColor(SmartDigitWidget::ColorType::Black); + m_total_digits[i]->setAmount(0.0); } // and now let's see what the table does. m_salt_table_model = new SaltTableModel(tableView_salts); @@ -185,12 +199,10 @@ void WaterDialog::setDigits() { double ppm = this->m_target->ppm(static_cast(i)); double min_ppm = ppm * 0.95; double max_ppm = ppm * 1.05; - QStringList msgs = QStringList() - << tr("Minimum expected concentration is %1 ppm").arg(min_ppm) - << tr("In range for target profile.") - << tr("Maximum expected concentration is %1 ppm").arg(max_ppm); m_ppm_digits[i]->setLimits(min_ppm,max_ppm); - m_ppm_digits[i]->setMessages(msgs); + m_ppm_digits[i]->setMessages(tr("Minimum expected concentration is %1 ppm").arg(min_ppm), + tr("In range for target profile."), + tr("Maximum expected concentration is %1 ppm").arg(max_ppm)); } // oddly, pH doesn't change with the target water @@ -198,7 +210,7 @@ void WaterDialog::setDigits() { } void WaterDialog::setRecipe(Recipe *rec) { - if ( rec == nullptr ) { + if (!rec) { return; } @@ -258,7 +270,7 @@ void WaterDialog::setRecipe(Recipe *rec) { if (ii->amountIsWeight()) { double lovi = ( ii->color_srm() +0.6 ) / 1.35; m_weighted_colors += (ii->amount()/m_total_grains)*lovi; - } + } break; case Fermentable::Type::Sugar: case Fermentable::Type::Other_Adjunct: @@ -362,7 +374,7 @@ void WaterDialog::newTotals() { for (int i = static_cast(Salt::Types::CACL2); i < static_cast(Salt::Types::LACTIC); ++i ) { Salt::Types type = static_cast(i); - m_total_digits[i]->setText(m_salt_table_model->total(type), 2); + m_total_digits[i]->setAmount(m_salt_table_model->total(type)); } // the total_* method return the numerator, we supply the denominator and @@ -380,15 +392,15 @@ void WaterDialog::newTotals() { for (int i = 0; i < static_cast(Water::Ions::numIons); ++i ) { Water::Ions ion = static_cast(i); double mPPM = modifier * this->m_base->ppm(ion); - m_ppm_digits[i]->setText( m_salt_table_model->total(ion) / allTheWaters + mPPM, 0 ); + m_ppm_digits[i]->setAmount( m_salt_table_model->total(ion) / allTheWaters + mPPM); } - btDigit_ph->setText( calculateMashpH(), 2 ); + btDigit_ph->setAmount(calculateMashpH()); } else { for (int i = 0; i < static_cast(Water::Ions::numIons); ++i ) { Water::Ions ion = static_cast(i); - m_ppm_digits[i]->setText( m_salt_table_model->total(ion) / allTheWaters, 0 ); + m_ppm_digits[i]->setAmount( m_salt_table_model->total(ion) / allTheWaters); } } return; @@ -511,7 +523,7 @@ double WaterDialog::calculateGristpH() { double lovi = 19.0; if ( ii->color_srm() <= 120 ) { lovi = ( ii->color_srm() + 0.6)/1.35; - } + } colorFromGrain = ( ii->amount() / m_total_grains ) * lovi; } break; diff --git a/src/WaterDialog.h b/src/WaterDialog.h index 4de647d4..f8771f7f 100644 --- a/src/WaterDialog.h +++ b/src/WaterDialog.h @@ -81,8 +81,8 @@ public slots: double calculateAddedSaltpH(); double calculateAcidpH(); - QVector m_ppm_digits; - QVector m_total_digits; + QVector m_ppm_digits; + QVector m_total_digits; WaterListModel * m_base_combo_list; WaterListModel * m_target_combo_list; SaltTableModel * m_salt_table_model; diff --git a/src/YeastDialog.cpp b/src/YeastDialog.cpp deleted file mode 100644 index 0134916b..00000000 --- a/src/YeastDialog.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/*====================================================================================================================== - * YeastDialog.cpp is part of Brewken, and is copyright the following authors 2009-2022: - * • Brian Rower - * • Daniel Pettersson - * • Matt Young - * • Mik Firestone - * • Philip Greggory Lee - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "YeastDialog.h" - -#include -#include -#include -#include -#include - -#include "database/ObjectStoreWrapper.h" -#include "MainWindow.h" -#include "model/Recipe.h" -#include "model/Yeast.h" -#include "tableModels/YeastTableModel.h" -#include "YeastEditor.h" -#include "YeastSortFilterProxyModel.h" - -YeastDialog::YeastDialog(MainWindow* parent) - : QDialog(parent), mainWindow(parent), yeastEditor(new YeastEditor(this)), numYeasts(0) -{ - doLayout(); - - yeastTableModel = new YeastTableModel(tableWidget, false); - yeastTableModel->setInventoryEditable(true); - yeastTableProxy = new YeastSortFilterProxyModel(tableWidget); - yeastTableProxy->setSourceModel(yeastTableModel); - tableWidget->setModel(yeastTableProxy); - tableWidget->setSortingEnabled(true); - tableWidget->sortByColumn( YEASTNAMECOL, Qt::AscendingOrder ); - yeastTableProxy->setDynamicSortFilter(true); - yeastTableProxy->setFilterKeyColumn(1); - - connect( pushButton_addToRecipe, SIGNAL( clicked() ), this, SLOT( addYeast() ) ); - connect( pushButton_edit, &QAbstractButton::clicked, this, &YeastDialog::editSelected ); - connect( pushButton_new, SIGNAL( clicked() ), this, SLOT( newYeast() ) ); - connect( pushButton_remove, &QAbstractButton::clicked, this, &YeastDialog::removeYeast ); - connect( tableWidget, &QAbstractItemView::doubleClicked, this, &YeastDialog::addYeast ); - connect( qLineEdit_searchBox, &QLineEdit::textEdited, this, &YeastDialog::filterYeasts); - - yeastTableModel->observeDatabase(true); - -} - -void YeastDialog::doLayout() -{ - resize(800, 300); - verticalLayout = new QVBoxLayout(this); - tableWidget = new QTableView(this); - horizontalLayout = new QHBoxLayout(); - horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - qLineEdit_searchBox = new QLineEdit(); - qLineEdit_searchBox->setMaxLength(30); - qLineEdit_searchBox->setPlaceholderText("Enter filter"); - pushButton_addToRecipe = new QPushButton(this); - pushButton_addToRecipe->setObjectName(QStringLiteral("pushButton_addToRecipe")); - pushButton_addToRecipe->setAutoDefault(false); - pushButton_addToRecipe->setDefault(true); - pushButton_new = new QPushButton(this); - pushButton_new->setObjectName(QStringLiteral("pushButton_new")); - pushButton_new->setAutoDefault(false); - pushButton_edit = new QPushButton(this); - pushButton_edit->setObjectName(QStringLiteral("pushButton_edit")); - QIcon icon; - icon.addFile(QStringLiteral(":/images/edit.svg"), QSize(), QIcon::Normal, QIcon::Off); - pushButton_edit->setIcon(icon); - pushButton_edit->setAutoDefault(false); - pushButton_remove = new QPushButton(this); - pushButton_remove->setObjectName(QStringLiteral("pushButton_remove")); - QIcon icon1; - icon1.addFile(QStringLiteral(":/images/smallMinus.svg"), QSize(), QIcon::Normal, QIcon::Off); - pushButton_remove->setIcon(icon1); - pushButton_remove->setAutoDefault(false); - horizontalLayout->addWidget(qLineEdit_searchBox); - horizontalLayout->addItem(horizontalSpacer); - horizontalLayout->addWidget(pushButton_addToRecipe); - horizontalLayout->addWidget(pushButton_new); - horizontalLayout->addWidget(pushButton_edit); - horizontalLayout->addWidget(pushButton_remove); - verticalLayout->addWidget(tableWidget); - verticalLayout->addLayout(horizontalLayout); - - retranslateUi(); - QMetaObject::connectSlotsByName(this); -} - -void YeastDialog::retranslateUi() -{ - setWindowTitle(tr("Yeast Database")); - pushButton_addToRecipe->setText(tr("Add to Recipe")); - pushButton_new->setText(tr("New")); - pushButton_edit->setText(QString()); - pushButton_remove->setText(QString()); -#ifndef QT_NO_TOOLTIP - pushButton_addToRecipe->setToolTip(tr("Add selected ingredient to recipe")); - pushButton_new->setToolTip(tr("Create new ingredient")); - pushButton_edit->setToolTip(tr("Edit selected ingredient")); - pushButton_remove->setToolTip(tr("Remove selected ingredient")); -#endif // QT_NO_TOOLTIP -} - -void YeastDialog::removeYeast() { - QModelIndexList selected = tableWidget->selectionModel()->selectedIndexes(); - int size = selected.size(); - if( size == 0 ) { - return; - } - - // Make sure only one row is selected. - int row = selected[0].row(); - for(int i = 1; i < size; ++i ) - { - if( selected[i].row() != row ) - return; - } - - // We need to translate from the view's index to the model's index. The - // proxy model does the heavy lifting, as long as we do the call. - QModelIndex translated = yeastTableProxy->mapToSource(selected[0]); - auto yeast = yeastTableModel->getRow(translated.row()); - ObjectStoreWrapper::softDelete(*yeast); - return; -} - -void YeastDialog::addYeast(const QModelIndex& index) -{ - QModelIndex translated; - - if( !index.isValid() ) - { - QModelIndexList selected = tableWidget->selectionModel()->selectedIndexes(); - int row, size, i; - - size = selected.size(); - if( size == 0 ) - return; - - // Make sure only one row is selected. - row = selected[0].row(); - for( i = 1; i < size; ++i ) - { - if( selected[i].row() != row ) - return; - } - - translated = yeastTableProxy->mapToSource(selected[0]); - } - else - { - // Only respond if the name is selected. Since we connect to double-click signal, - // this keeps us from adding something to the recipe when we just want to edit - // one of the other columns. - if( index.column() == YEASTNAMECOL ) - translated = yeastTableProxy->mapToSource(index); - else - return; - } - - // Adds a copy of yeast. - MainWindow::instance().addYeastToRecipe(yeastTableModel->getRow(translated.row())); - - return; -} - -void YeastDialog::editSelected() { - QModelIndexList selected = tableWidget->selectionModel()->selectedIndexes(); - int size = selected.size(); - if (size == 0) { - return; - } - - // Make sure only one row is selected. - int row = selected[0].row(); - - for (int i = 1; i < size; ++i ) { - if (selected[i].row() != row ) { - return; - } - } - QModelIndex translated = yeastTableProxy->mapToSource(selected[0]); - auto yeast = yeastTableModel->getRow(translated.row()); - yeastEditor->setYeast(yeast.get()); - yeastEditor->show(); - return; -} - -void YeastDialog::newYeast() -{ - newYeast(QString()); -} - -void YeastDialog::newYeast(QString folder) -{ - QString name = QInputDialog::getText(this, tr("Yeast name"), - tr("Yeast name:")); - if( name.isEmpty() ) - return; - - Yeast* y = new Yeast(name); - if ( ! folder.isEmpty() ) - y->setFolder(folder); - - yeastEditor->setYeast(y); - yeastEditor->show(); -} - -void YeastDialog::filterYeasts(QString searchExpression) -{ - yeastTableProxy->setFilterCaseSensitivity(Qt::CaseInsensitive); - yeastTableProxy->setFilterFixedString(searchExpression); -} diff --git a/src/YeastEditor.cpp b/src/YeastEditor.cpp deleted file mode 100644 index 2609fa47..00000000 --- a/src/YeastEditor.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/*====================================================================================================================== - * YeastEditor.cpp is part of Brewken, and is copyright the following authors 2009-2023: - * • Brian Rower - * • Kregg Kemper - * • Matt Young - * • Mik Firestone - * • Philip Greggory Lee - * • Samuel Östling - * - * Brewken is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * Brewken is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied - * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more - * details. - * - * You should have received a copy of the GNU General Public License along with this program. If not, see - * . - =====================================================================================================================*/ -#include "YeastEditor.h" - -#include - -#include - -#include "BtHorizontalTabs.h" -#include "config.h" -#include "database/ObjectStoreWrapper.h" -#include "measurement/Unit.h" -#include "model/Yeast.h" - -YeastEditor::YeastEditor(QWidget * parent) : - QDialog(parent), - obsYeast(nullptr) { - setupUi(this); - - tabWidget_editor->tabBar()->setStyle(new BtHorizontalTabs); - // Note, per https://wiki.qt.io/New_Signal_Slot_Syntax#Default_arguments_in_slot, the use of a trivial lambda - // function to allow use of default argument on newYeast() slot - connect(pushButton_new, &QAbstractButton::clicked, this, [this]() { this->newYeast(); return; } ); - connect(pushButton_save, &QAbstractButton::clicked, this, &YeastEditor::save ); - connect(pushButton_cancel, &QAbstractButton::clicked, this, &YeastEditor::clearAndClose ); - return; -} - -YeastEditor::~YeastEditor() = default; - -void YeastEditor::setYeast(Yeast * y) { - if (this->obsYeast) { - disconnect(this->obsYeast, nullptr, this, nullptr); - } - - this->obsYeast = y; - if (this->obsYeast) { - connect(this->obsYeast, SIGNAL(changed(QMetaProperty, QVariant)), this, SLOT(changed(QMetaProperty, QVariant))); - showChanges(); - } -} - -void YeastEditor::save() { - if (!this->obsYeast) { - setVisible(false); - return; - } - - this->obsYeast->setName (lineEdit_name ->text() ); - this->obsYeast->setType (static_cast(comboBox_type->currentIndex()) ); - this->obsYeast->setForm (static_cast(comboBox_form->currentIndex()) ); - this->obsYeast->setAmountIsWeight (checkBox_amountIsWeight->checkState() == Qt::Checked ); - this->obsYeast->setLaboratory (lineEdit_laboratory ->text() ); - this->obsYeast->setProductID (lineEdit_productID ->text() ); - this->obsYeast->setMinTemperature_c(lineEdit_minTemperature->toCanonical().quantity() ); - this->obsYeast->setMaxTemperature_c(lineEdit_maxTemperature->toCanonical().quantity() ); - this->obsYeast->setFlocculation (static_cast(comboBox_flocculation->currentIndex())); - this->obsYeast->setAttenuation_pct (lineEdit_attenuation ->getValueAs() ); - this->obsYeast->setTimesCultured (lineEdit_timesCultured ->getValueAs() ); - this->obsYeast->setMaxReuse (lineEdit_maxReuse ->getValueAs() ); - this->obsYeast->setAddToSecondary (checkBox_addToSecondary->checkState() == Qt::Checked ); - this->obsYeast->setBestFor (textEdit_bestFor ->toPlainText() ); - this->obsYeast->setNotes (textEdit_notes ->toPlainText() ); - - if (this->obsYeast->key() < 0) { - ObjectStoreWrapper::insert(*this->obsYeast); - } - // do this late to make sure we've the row in the inventory table - this->obsYeast->setInventoryQuanta(lineEdit_inventory->text().toInt()); - setVisible(false); - return; -} - -void YeastEditor::clearAndClose() { - setYeast(nullptr); - setVisible(false); // Hide the window. -} - -void YeastEditor::changed(QMetaProperty prop, QVariant /*val*/) { - if (sender() == obsYeast) { - showChanges(&prop); - } -} - -void YeastEditor::showChanges(QMetaProperty * metaProp) { - Yeast * y = obsYeast; - if (y == nullptr) { - return; - } - - QString propName; - QVariant value; - bool updateAll = false; - if (metaProp == nullptr) { - updateAll = true; - } else { - propName = metaProp->name(); - value = metaProp->read(y); - } - - if (propName == PropertyNames::NamedEntity::name || updateAll) { - lineEdit_name->setText(obsYeast->name()); - lineEdit_name->setCursorPosition(0); - - tabWidget_editor->setTabText(0, obsYeast->name()); - if (!updateAll) { - return; - } - } - if (updateAll || propName == PropertyNames::Yeast::type ) { comboBox_type->setCurrentIndex(static_cast(obsYeast->type())); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::form ) { comboBox_form->setCurrentIndex(static_cast(obsYeast->form())); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::NamedEntityWithInventory::inventory) { lineEdit_inventory ->setText(obsYeast->inventory () , 0); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::amountIsWeight ) { checkBox_amountIsWeight->setCheckState((obsYeast->amountIsWeight()) ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::laboratory ) { lineEdit_laboratory ->setText(obsYeast->laboratory () ); lineEdit_laboratory->setCursorPosition(0); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::productID ) { lineEdit_productID ->setText(obsYeast->productID () ); lineEdit_productID ->setCursorPosition(0); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::minTemperature_c ) { lineEdit_minTemperature->setText(obsYeast->minTemperature_c() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::maxTemperature_c ) { lineEdit_maxTemperature->setText(obsYeast->maxTemperature_c() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::flocculation ) { comboBox_flocculation ->setCurrentIndex(static_cast(obsYeast->flocculation()) ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::attenuation_pct ) { lineEdit_attenuation ->setText(obsYeast->attenuation_pct() ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::timesCultured ) { lineEdit_timesCultured ->setText(obsYeast->timesCultured () , 0); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::maxReuse ) { lineEdit_maxReuse ->setText(obsYeast->maxReuse () , 0); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::addToSecondary ) { checkBox_addToSecondary->setCheckState((obsYeast->addToSecondary()) ? Qt::Checked : Qt::Unchecked); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::bestFor ) { textEdit_bestFor ->setPlainText(obsYeast->bestFor () ); if (!updateAll) { return; } } - if (updateAll || propName == PropertyNames::Yeast::notes ) { textEdit_notes ->setPlainText(obsYeast->notes () ); if (!updateAll) { return; } } - return; -} - -void YeastEditor::newYeast(QString folder) { - QString name = QInputDialog::getText(this, tr("Yeast name"), tr("Yeast name:")); - if (name.isEmpty()) { - return; - } - - // .:TODO:. Change to shared_ptr as currently leads to memory leak in clearAndClose() - Yeast * y = new Yeast(name); - if (! folder.isEmpty()) { - y->setFolder(folder); - } - - setYeast(y); - show(); - return; -} diff --git a/src/YeastSortFilterProxyModel.cpp b/src/YeastSortFilterProxyModel.cpp index b06c1571..6e010f10 100644 --- a/src/YeastSortFilterProxyModel.cpp +++ b/src/YeastSortFilterProxyModel.cpp @@ -1,5 +1,5 @@ /*====================================================================================================================== - * YeastSortFilterProxyModel.cpp is part of Brewken, and is copyright the following authors 2009-2022: + * YeastSortFilterProxyModel.cpp is part of Brewken, and is copyright the following authors 2009-2023: * • Daniel Pettersson * • Matt Young * • Mik Firestone @@ -38,8 +38,9 @@ bool YeastSortFilterProxyModel::lessThan(const QModelIndex &left, QVariant rightYeast = sourceModel()->data(right); double lAmt, rAmt; - switch (left.column()) { - case YEASTINVENTORYCOL: + auto const columnIndex = static_cast(left.column()); + switch (columnIndex) { + case YeastTableModel::ColumnIndex::Inventory: if (Measurement::qStringToSI(leftYeast.toString(), Measurement::PhysicalQuantity::Volume).quantity() == 0.0 && this->sortOrder() == Qt::AscendingOrder) { return false; @@ -50,10 +51,10 @@ bool YeastSortFilterProxyModel::lessThan(const QModelIndex &left, // This is a lie. I need to figure out if they are weights or volumes. // and then figure some reasonable way to compare weights to volumes. // Maybe lying isn't such a bad idea - case YEASTAMOUNTCOL: + case YeastTableModel::ColumnIndex::Amount: return Measurement::qStringToSI(leftYeast.toString(), Measurement::PhysicalQuantity::Volume) < Measurement::qStringToSI(rightYeast.toString(), Measurement::PhysicalQuantity::Volume); - case YEASTPRODIDCOL: + case YeastTableModel::ColumnIndex::ProdId: lAmt = Localization::toDouble(leftYeast.toString(), Q_FUNC_INFO); rAmt = Localization::toDouble(rightYeast.toString(), Q_FUNC_INFO); return lAmt < rAmt; diff --git a/src/database/Database.cpp b/src/database/Database.cpp index 937c2eca..8f8e9300 100644 --- a/src/database/Database.cpp +++ b/src/database/Database.cpp @@ -65,10 +65,10 @@ namespace { EnumStringMapping const dbTypeToName { - {Database::tr("NODB" ), Database::DbType::NODB }, - {Database::tr("SQLITE"), Database::DbType::SQLITE}, - {Database::tr("PGSQL" ), Database::DbType::PGSQL }, - {Database::tr("ALLDB" ), Database::DbType::ALLDB }, + {Database::DbType::NODB , Database::tr("NODB" )}, + {Database::DbType::SQLITE, Database::tr("SQLITE")}, + {Database::DbType::PGSQL , Database::tr("PGSQL" )}, + {Database::DbType::ALLDB , Database::tr("ALLDB" )}, }; // diff --git a/src/database/DatabaseSchemaHelper.cpp b/src/database/DatabaseSchemaHelper.cpp index 7a53ef4d..64745c20 100644 --- a/src/database/DatabaseSchemaHelper.cpp +++ b/src/database/DatabaseSchemaHelper.cpp @@ -547,7 +547,7 @@ namespace { {QString( "UPDATE hop SET form = 'leaf' WHERE form = 'Leaf'" )}, {QString("ALTER TABLE hop ADD COLUMN producer %1").arg(db.getDbNativeTypeName())}, {QString("ALTER TABLE hop ADD COLUMN product_id %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE hop ADD COLUMN year %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE hop ADD COLUMN year %1").arg(db.getDbNativeTypeName())}, {QString("ALTER TABLE hop ADD COLUMN total_oil_ml_per_100g %1").arg(db.getDbNativeTypeName())}, {QString("ALTER TABLE hop ADD COLUMN farnesene_pct %1").arg(db.getDbNativeTypeName())}, {QString("ALTER TABLE hop ADD COLUMN geraniol_pct %1").arg(db.getDbNativeTypeName())}, @@ -567,24 +567,48 @@ namespace { {QString(" UPDATE fermentable SET ftype = 'extract' WHERE ftype = 'Extract'" )}, {QString(" UPDATE fermentable SET ftype = 'dry extract' WHERE ftype = 'Dry Extract'")}, {QString(" UPDATE fermentable SET ftype = 'other' WHERE ftype = 'Adjunct'" )}, - {QString("ALTER TABLE fermentable ADD COLUMN grain_group %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN producer %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN productId %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN fineGrindYield_pct %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN coarseGrindYield_pct %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN potentialYield_sg %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN alphaAmylase_dextUnits %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN kolbachIndex_pct %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN amountIsWeight %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN hardnessPrpGlassy_pct %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN hardnessPrpHalf_pct %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN hardnessPrpMealy_pct %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN kernelSizePrpPlump_pct %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN kernelSizePrpThin_pct %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN friability_pct %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN di_ph %1").arg(db.getDbNativeTypeName())}, - {QString("ALTER TABLE fermentable ADD COLUMN viscosity_cP %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN grain_group %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN producer %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN productId %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN fineGrindYield_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN coarseGrindYield_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN potentialYield_sg %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN alphaAmylase_dextUnits %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN kolbachIndex_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN amountIsWeight %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN hardnessPrpGlassy_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN hardnessPrpHalf_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN hardnessPrpMealy_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN kernelSizePrpPlump_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN kernelSizePrpThin_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN friability_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN di_ph %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN viscosity_cP %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN dmsP %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN dmsPIsMassPerVolume %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN fan %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN fanIsMassPerVolume %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN fermentability_pct %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN betaGlucan %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE fermentable ADD COLUMN betaGlucanIsMassPerVolume %1").arg(db.getDbNativeTypeName())}, {QString(" UPDATE fermentable SET amountIsWeight = ?"), {QVariant{true}}}, // All existing amounts will be weights + // + // Misc: Extended and additional fields for BeerJSON + // + // We only need to update the old Misc type mappings. The new ones should "just work". + {QString(" UPDATE misc SET mtype = 'spice' WHERE mtype = 'Spice' ")}, + {QString(" UPDATE misc SET mtype = 'fining' WHERE mtype = 'Fining' ")}, + {QString(" UPDATE misc SET mtype = 'water agent' WHERE mtype = 'Water Agent'")}, + {QString(" UPDATE misc SET mtype = 'herb' WHERE mtype = 'Herb' ")}, + {QString(" UPDATE misc SET mtype = 'flavor' WHERE mtype = 'Flavor' ")}, + {QString(" UPDATE misc SET mtype = 'other' WHERE mtype = 'Other' ")}, + {QString("ALTER TABLE misc ADD COLUMN producer %1").arg(db.getDbNativeTypeName())}, + {QString("ALTER TABLE misc ADD COLUMN product_id %1").arg(db.getDbNativeTypeName())}, + + + + + }; return executeSqlQueries(q, migrationQueries); } diff --git a/src/database/ObjectStore.cpp b/src/database/ObjectStore.cpp index 19a19474..5df0d6e0 100644 --- a/src/database/ObjectStore.cpp +++ b/src/database/ObjectStore.cpp @@ -34,9 +34,6 @@ #include "model/NamedParameterBundle.h" #include "utils/OptionalHelpers.h" -//// DELETE THIS! -#include "model/Water.h" - // Private implementation details that don't need access to class member variables namespace { @@ -86,9 +83,9 @@ namespace { // ... // bug DATE // ); - // .:TBD:. At some future point we might extend our model to allow marking some columns as NOT NULL (eg via some - // making the derived class of NamedEntity available in ObjectStore::TableDefinition so we can query - // isOptional() on each property), but it doesn't seem pressing at the moment. + // .:TBD:. At some future point we might extend our model to allow marking some columns as NOT NULL (eg by making + // the derived class of NamedEntity available in ObjectStore::TableDefinition so we can query isOptional() + // on each property), but it doesn't seem pressing at the moment. // QString queryString{"CREATE TABLE "}; QTextStream queryStringAsStream{&queryString}; @@ -229,7 +226,6 @@ namespace { ObjectStore::TableField const & fieldDefn, QString const & stringValue) { // It's a coding error if we called this function for a non-enum field - // It's OK to be called for EnumOpt as stringToEnumOpt() calls us to avoid duplication Q_ASSERT(fieldDefn.fieldType == ObjectStore::FieldType::Enum); Q_ASSERT(fieldDefn.enumMapping != nullptr); auto match = fieldDefn.enumMapping->stringToEnumAsInt(stringValue); @@ -512,18 +508,22 @@ namespace { * case of SQLite). * - Some integer foreign key columns were created without a type in SQLite, which means they get treated as * strings + * - In another case, some foreign keys were created as a double instead of an int * Rather than just say anything goes, we store the known problem columns here and log a warning about them. */ QMap> const legacyBadTypes { {{"equipment", "calc_boil_volume", ObjectStore::FieldType::Bool}, {QMetaType::QString}}, {{"fermentable", "add_after_boil" , ObjectStore::FieldType::Bool}, {QMetaType::QString}}, + {{"fermentable", "inventory_id" , ObjectStore::FieldType::Int }, {QMetaType::Double }}, {{"fermentable", "is_mashed" , ObjectStore::FieldType::Bool}, {QMetaType::QString}}, {{"fermentable", "recommend_mash" , ObjectStore::FieldType::Bool}, {QMetaType::QString}}, + {{"hop", "inventory_id" , ObjectStore::FieldType::Int }, {QMetaType::Double }}, {{"mash", "equip_adjust" , ObjectStore::FieldType::Bool}, {QMetaType::QString}}, {{"misc", "amount_is_weight", ObjectStore::FieldType::Bool}, {QMetaType::QString}}, {{"misc", "inventory_id" , ObjectStore::FieldType::Int }, {QMetaType::QString}}, {{"yeast", "add_to_secondary", ObjectStore::FieldType::Bool}, {QMetaType::QString}}, {{"yeast", "amount_is_weight", ObjectStore::FieldType::Bool}, {QMetaType::QString}}, + {{"yeast", "inventory_id" , ObjectStore::FieldType::Int }, {QMetaType::Double }}, }; } @@ -683,12 +683,14 @@ class ObjectStore::impl { TableColumnAndType tableColumnAndType{*primaryTable.tableName, *fieldDefn.columnName, fieldDefn.fieldType}; if (legacyBadTypes.contains(tableColumnAndType) && legacyBadTypes.value(tableColumnAndType).contains(propertyType)) { - // It's technically wrong but we know about it and it works, so just log a warning - qWarning() << - Q_FUNC_INFO << fieldDefn.fieldType << "property" << fieldDefn.propertyName << "on table" << - primaryTable.tableName << "(value " << propertyValue << ") is stored as " << - propertyValue.typeName() << "(" << propertyType << ") in column" << fieldDefn.columnName << - ". This is a known ugliness that we intend to fix one day."; + // It's technically wrong but we know about it and it works, so just log it. If this logging is + // uncommented, you can get a list of all the things we need to fix with: + // grep "known ugliness" *.log | sed 's/^.*property /Property /; s/This is a known ugliness .*$//' | sort -u +/// qDebug() << +/// Q_FUNC_INFO << fieldDefn.fieldType << "property" << fieldDefn.propertyName << "on table" << +/// primaryTable.tableName << "(value " << propertyValue << ") is stored as " << +/// propertyValue.typeName() << "(" << propertyType << ") in column" << fieldDefn.columnName << +/// ". This is a known ugliness that we intend to fix one day."; } else { // It's not a known exception, so it's a coding error qCritical() << @@ -760,9 +762,9 @@ class ObjectStore::impl { if (propertyValue.isNull()) { // This is either a coding error or someone messed with the DB data. qCritical() << - Q_FUNC_INFO << "Found null value for non-optional enum when mapping column " << fieldDefn.columnName << - " to property " << fieldDefn.propertyName << "for" << primaryTable.tableName << - "so using 0"; + Q_FUNC_INFO << "Found null value for non-optional enum when mapping column " << + fieldDefn.columnName << " to property " << fieldDefn.propertyName << "for" << + primaryTable.tableName << "so using 0"; propertyValue = QVariant(0); return; } diff --git a/src/database/ObjectStore.h b/src/database/ObjectStore.h index 6ed17648..956da49b 100644 --- a/src/database/ObjectStore.h +++ b/src/database/ObjectStore.h @@ -29,6 +29,7 @@ #include "model/NamedEntity.h" #include "utils/BtStringConst.h" #include "utils/EnumStringMapping.h" +#include "utils/NoCopy.h" #include "utils/TypeLookup.h" class Database; @@ -495,15 +496,8 @@ class ObjectStore : public QObject { class impl; std::unique_ptr pimpl; - //! No copy constructor, as never want anyone, not even our friends, to make copies of a singleton - ObjectStore(ObjectStore const &) = delete; - //! No assignment operator , as never want anyone, not even our friends, to make copies of a singleton. - ObjectStore & operator=(ObjectStore const &) = delete; - //! No move constructor - ObjectStore(ObjectStore &&) = delete; - //! No move assignment - ObjectStore & operator=(ObjectStore &&) = delete; - + // Insert all the usual boilerplate to prevent copy/assignment/move + NO_COPY_DECLARATIONS(ObjectStore) }; diff --git a/src/database/ObjectStoreTyped.cpp b/src/database/ObjectStoreTyped.cpp index 611b17b1..c42913c8 100644 --- a/src/database/ObjectStoreTyped.cpp +++ b/src/database/ObjectStoreTyped.cpp @@ -54,28 +54,28 @@ namespace { template<> ObjectStore::TableDefinition const PRIMARY_TABLE { "equipment", { - {ObjectStore::FieldType::Int, "id", PropertyNames::NamedEntity::key}, - {ObjectStore::FieldType::String, "name", PropertyNames::NamedEntity::name}, - {ObjectStore::FieldType::Bool, "display", PropertyNames::NamedEntity::display}, - {ObjectStore::FieldType::Bool, "deleted", PropertyNames::NamedEntity::deleted}, - {ObjectStore::FieldType::String, "folder", PropertyNames::NamedEntity::folder}, - {ObjectStore::FieldType::Double, "batch_size", PropertyNames::Equipment::batchSize_l}, - {ObjectStore::FieldType::Double, "boiling_point", PropertyNames::Equipment::boilingPoint_c}, - {ObjectStore::FieldType::Double, "boil_size", PropertyNames::Equipment::boilSize_l}, - {ObjectStore::FieldType::Double, "boil_time", PropertyNames::Equipment::boilTime_min}, - {ObjectStore::FieldType::Bool, "calc_boil_volume", PropertyNames::Equipment::calcBoilVolume}, - {ObjectStore::FieldType::Double, "real_evap_rate", PropertyNames::Equipment::evapRate_lHr}, - {ObjectStore::FieldType::Double, "evap_rate", PropertyNames::Equipment::evapRate_pctHr}, - {ObjectStore::FieldType::Double, "absorption", PropertyNames::Equipment::grainAbsorption_LKg}, - {ObjectStore::FieldType::Double, "hop_utilization", PropertyNames::Equipment::hopUtilization_pct}, - {ObjectStore::FieldType::Double, "lauter_deadspace", PropertyNames::Equipment::lauterDeadspace_l}, - {ObjectStore::FieldType::String, "notes", PropertyNames::Equipment::notes}, - {ObjectStore::FieldType::Double, "top_up_kettle", PropertyNames::Equipment::topUpKettle_l}, - {ObjectStore::FieldType::Double, "top_up_water", PropertyNames::Equipment::topUpWater_l}, - {ObjectStore::FieldType::Double, "trub_chiller_loss", PropertyNames::Equipment::trubChillerLoss_l}, + {ObjectStore::FieldType::Int , "id" , PropertyNames::NamedEntity::key }, + {ObjectStore::FieldType::String, "name" , PropertyNames::NamedEntity::name }, + {ObjectStore::FieldType::Bool , "display" , PropertyNames::NamedEntity::display }, + {ObjectStore::FieldType::Bool , "deleted" , PropertyNames::NamedEntity::deleted }, + {ObjectStore::FieldType::String, "folder" , PropertyNames::NamedEntity::folder }, + {ObjectStore::FieldType::Double, "batch_size" , PropertyNames::Equipment::batchSize_l }, + {ObjectStore::FieldType::Double, "boiling_point" , PropertyNames::Equipment::boilingPoint_c }, + {ObjectStore::FieldType::Double, "boil_size" , PropertyNames::Equipment::boilSize_l }, + {ObjectStore::FieldType::Double, "boil_time" , PropertyNames::Equipment::boilTime_min }, + {ObjectStore::FieldType::Bool , "calc_boil_volume" , PropertyNames::Equipment::calcBoilVolume }, + {ObjectStore::FieldType::Double, "real_evap_rate" , PropertyNames::Equipment::evapRate_lHr }, + {ObjectStore::FieldType::Double, "evap_rate" , PropertyNames::Equipment::evapRate_pctHr }, + {ObjectStore::FieldType::Double, "absorption" , PropertyNames::Equipment::grainAbsorption_LKg }, + {ObjectStore::FieldType::Double, "hop_utilization" , PropertyNames::Equipment::hopUtilization_pct }, + {ObjectStore::FieldType::Double, "lauter_deadspace" , PropertyNames::Equipment::lauterDeadspace_l }, + {ObjectStore::FieldType::String, "notes" , PropertyNames::Equipment::notes }, + {ObjectStore::FieldType::Double, "top_up_kettle" , PropertyNames::Equipment::topUpKettle_l }, + {ObjectStore::FieldType::Double, "top_up_water" , PropertyNames::Equipment::topUpWater_l }, + {ObjectStore::FieldType::Double, "trub_chiller_loss", PropertyNames::Equipment::trubChillerLoss_l }, {ObjectStore::FieldType::Double, "tun_specific_heat", PropertyNames::Equipment::tunSpecificHeat_calGC}, - {ObjectStore::FieldType::Double, "tun_volume", PropertyNames::Equipment::tunVolume_l}, - {ObjectStore::FieldType::Double, "tun_weight", PropertyNames::Equipment::tunWeight_kg} + {ObjectStore::FieldType::Double, "tun_volume" , PropertyNames::Equipment::tunVolume_l }, + {ObjectStore::FieldType::Double, "tun_weight" , PropertyNames::Equipment::tunWeight_kg }, } }; template<> ObjectStore::JunctionTableDefinitions const JUNCTION_TABLES { @@ -85,7 +85,7 @@ namespace { { {ObjectStore::FieldType::Int, "id" }, {ObjectStore::FieldType::Int, "child_id", PropertyNames::NamedEntity::key, nullptr, &PRIMARY_TABLE}, - {ObjectStore::FieldType::Int, "parent_id", PropertyNames::NamedEntity::parentKey, nullptr, &PRIMARY_TABLE} + {ObjectStore::FieldType::Int, "parent_id", PropertyNames::NamedEntity::parentKey, nullptr, &PRIMARY_TABLE}, }, ObjectStore::MAX_ONE_ENTRY } @@ -133,22 +133,29 @@ namespace { {ObjectStore::FieldType::Bool, "recommend_mash" , PropertyNames::Fermentable::recommendMash }, {ObjectStore::FieldType::Double, "yield" , PropertyNames::Fermentable::yield_pct }, // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ - {ObjectStore::FieldType::Enum, "grain_group" , PropertyNames::Fermentable::grainGroup, &Fermentable::grainGroupStringMapping}, - {ObjectStore::FieldType::String, "producer" , PropertyNames::Fermentable::producer }, - {ObjectStore::FieldType::String, "productId" , PropertyNames::Fermentable::productId }, - {ObjectStore::FieldType::Double, "fineGrindYield_pct" , PropertyNames::Fermentable::fineGrindYield_pct }, - {ObjectStore::FieldType::Double, "coarseGrindYield_pct" , PropertyNames::Fermentable::coarseGrindYield_pct }, - {ObjectStore::FieldType::Double, "potentialYield_sg" , PropertyNames::Fermentable::potentialYield_sg }, - {ObjectStore::FieldType::Double, "alphaAmylase_dextUnits", PropertyNames::Fermentable::alphaAmylase_dextUnits }, - {ObjectStore::FieldType::Double, "kolbachIndex_pct" , PropertyNames::Fermentable::kolbachIndex_pct }, - {ObjectStore::FieldType::Double, "hardnessPrpGlassy_pct" , PropertyNames::Fermentable::hardnessPrpGlassy_pct }, - {ObjectStore::FieldType::Double, "hardnessPrpHalf_pct" , PropertyNames::Fermentable::hardnessPrpHalf_pct }, - {ObjectStore::FieldType::Double, "hardnessPrpMealy_pct" , PropertyNames::Fermentable::hardnessPrpMealy_pct }, - {ObjectStore::FieldType::Double, "kernelSizePrpPlump_pct", PropertyNames::Fermentable::kernelSizePrpPlump_pct }, - {ObjectStore::FieldType::Double, "kernelSizePrpThin_pct" , PropertyNames::Fermentable::kernelSizePrpThin_pct }, - {ObjectStore::FieldType::Double, "friability_pct" , PropertyNames::Fermentable::friability_pct }, - {ObjectStore::FieldType::Double, "di_ph" , PropertyNames::Fermentable::di_ph }, - {ObjectStore::FieldType::Double, "viscosity_cP" , PropertyNames::Fermentable::viscosity_cP }, + {ObjectStore::FieldType::Enum, "grain_group" , PropertyNames::Fermentable::grainGroup, &Fermentable::grainGroupStringMapping}, + {ObjectStore::FieldType::String, "producer" , PropertyNames::Fermentable::producer }, + {ObjectStore::FieldType::String, "productId" , PropertyNames::Fermentable::productId }, + {ObjectStore::FieldType::Double, "fineGrindYield_pct" , PropertyNames::Fermentable::fineGrindYield_pct }, + {ObjectStore::FieldType::Double, "coarseGrindYield_pct" , PropertyNames::Fermentable::coarseGrindYield_pct }, + {ObjectStore::FieldType::Double, "potentialYield_sg" , PropertyNames::Fermentable::potentialYield_sg }, + {ObjectStore::FieldType::Double, "alphaAmylase_dextUnits" , PropertyNames::Fermentable::alphaAmylase_dextUnits }, + {ObjectStore::FieldType::Double, "kolbachIndex_pct" , PropertyNames::Fermentable::kolbachIndex_pct }, + {ObjectStore::FieldType::Double, "hardnessPrpGlassy_pct" , PropertyNames::Fermentable::hardnessPrpGlassy_pct }, + {ObjectStore::FieldType::Double, "hardnessPrpHalf_pct" , PropertyNames::Fermentable::hardnessPrpHalf_pct }, + {ObjectStore::FieldType::Double, "hardnessPrpMealy_pct" , PropertyNames::Fermentable::hardnessPrpMealy_pct }, + {ObjectStore::FieldType::Double, "kernelSizePrpPlump_pct" , PropertyNames::Fermentable::kernelSizePrpPlump_pct }, + {ObjectStore::FieldType::Double, "kernelSizePrpThin_pct" , PropertyNames::Fermentable::kernelSizePrpThin_pct }, + {ObjectStore::FieldType::Double, "friability_pct" , PropertyNames::Fermentable::friability_pct }, + {ObjectStore::FieldType::Double, "di_ph" , PropertyNames::Fermentable::di_ph }, + {ObjectStore::FieldType::Double, "viscosity_cP" , PropertyNames::Fermentable::viscosity_cP }, + {ObjectStore::FieldType::Double, "dmsP" , PropertyNames::Fermentable::dmsP }, + {ObjectStore::FieldType::Bool , "dmsPIsMassPerVolume" , PropertyNames::Fermentable::dmsPIsMassPerVolume }, + {ObjectStore::FieldType::Double, "fan" , PropertyNames::Fermentable::fan }, + {ObjectStore::FieldType::Bool , "fanIsMassPerVolume" , PropertyNames::Fermentable::fanIsMassPerVolume }, + {ObjectStore::FieldType::Double, "fermentability_pct" , PropertyNames::Fermentable::fermentability_pct }, + {ObjectStore::FieldType::Double, "betaGlucan" , PropertyNames::Fermentable::betaGlucan }, + {ObjectStore::FieldType::Bool , "betaGlucanIsMassPerVolume", PropertyNames::Fermentable::betaGlucanIsMassPerVolume}, } }; template<> ObjectStore::JunctionTableDefinitions const JUNCTION_TABLES { @@ -157,7 +164,7 @@ namespace { { {ObjectStore::FieldType::Int, "id" }, {ObjectStore::FieldType::Int, "child_id", PropertyNames::NamedEntity::key, nullptr, &PRIMARY_TABLE}, - {ObjectStore::FieldType::Int, "parent_id", PropertyNames::NamedEntity::parentKey, nullptr, &PRIMARY_TABLE} + {ObjectStore::FieldType::Int, "parent_id", PropertyNames::NamedEntity::parentKey, nullptr, &PRIMARY_TABLE}, }, ObjectStore::MAX_ONE_ENTRY } @@ -169,8 +176,8 @@ namespace { template<> ObjectStore::TableDefinition const PRIMARY_TABLE { "hop_in_inventory", { - {ObjectStore::FieldType::Int, "id", PropertyNames::Inventory::id}, - {ObjectStore::FieldType::Double, "amount", PropertyNames::Inventory::amount} + {ObjectStore::FieldType::Int, "id", PropertyNames::Inventory::id }, + {ObjectStore::FieldType::Double, "amount", PropertyNames::Inventory::amount}, } }; template<> ObjectStore::JunctionTableDefinitions const JUNCTION_TABLES {}; @@ -205,7 +212,7 @@ namespace { // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ {ObjectStore::FieldType::String, "producer", PropertyNames::Hop::producer }, {ObjectStore::FieldType::String, "product_id", PropertyNames::Hop::product_id }, - {ObjectStore::FieldType::Int, "year", PropertyNames::Hop::year }, + {ObjectStore::FieldType::String, "year", PropertyNames::Hop::year }, {ObjectStore::FieldType::Double, "total_oil_ml_per_100g", PropertyNames::Hop::total_oil_ml_per_100g }, {ObjectStore::FieldType::Double, "farnesene_pct", PropertyNames::Hop::farnesene_pct }, {ObjectStore::FieldType::Double, "geraniol_pct", PropertyNames::Hop::geraniol_pct }, @@ -224,7 +231,7 @@ namespace { { {ObjectStore::FieldType::Int, "id" }, {ObjectStore::FieldType::Int, "child_id", PropertyNames::NamedEntity::key, nullptr, &PRIMARY_TABLE}, - {ObjectStore::FieldType::Int, "parent_id", PropertyNames::NamedEntity::parentKey, nullptr, &PRIMARY_TABLE} + {ObjectStore::FieldType::Int, "parent_id", PropertyNames::NamedEntity::parentKey, nullptr, &PRIMARY_TABLE}, }, ObjectStore::MAX_ONE_ENTRY } @@ -245,7 +252,7 @@ namespace { {ObjectStore::FieldType::Bool, "hasTimer", PropertyNames::Instruction::hasTimer }, {ObjectStore::FieldType::String, "timervalue", PropertyNames::Instruction::timerValue}, {ObjectStore::FieldType::Bool, "completed", PropertyNames::Instruction::completed }, - {ObjectStore::FieldType::Double, "interval", PropertyNames::Instruction::interval } + {ObjectStore::FieldType::Double, "interval", PropertyNames::Instruction::interval }, } }; // Instructions don't have children @@ -280,11 +287,11 @@ namespace { // NB: MashSteps don't get folders, because they don't separate from their Mash /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// EnumStringMapping const MASH_STEP_TYPE_ENUM { - {"Infusion", MashStep::Type::Infusion}, - {"Temperature", MashStep::Type::Temperature}, - {"Decoction", MashStep::Type::Decoction}, - {"FlySparge", MashStep::Type::flySparge}, - {"BatchSparge", MashStep::Type::batchSparge} + {MashStep::Type::Infusion , "Infusion" }, + {MashStep::Type::Temperature, "Temperature"}, + {MashStep::Type::Decoction , "Decoction" }, + {MashStep::Type::flySparge , "FlySparge" }, + {MashStep::Type::batchSparge, "BatchSparge"} }; template<> ObjectStore::TableDefinition const PRIMARY_TABLE { "mashstep", @@ -303,7 +310,7 @@ namespace { {ObjectStore::FieldType::Double, "ramp_time", PropertyNames::MashStep::rampTime_min }, {ObjectStore::FieldType::Int, "step_number", PropertyNames::MashStep::stepNumber }, {ObjectStore::FieldType::Double, "step_temp", PropertyNames::MashStep::stepTemp_c }, - {ObjectStore::FieldType::Double, "step_time", PropertyNames::MashStep::stepTime_min } + {ObjectStore::FieldType::Double, "step_time", PropertyNames::MashStep::stepTime_min }, } }; // MashSteps don't have children @@ -315,8 +322,8 @@ namespace { template<> ObjectStore::TableDefinition const PRIMARY_TABLE { "misc_in_inventory", { - {ObjectStore::FieldType::Int, "id", PropertyNames::Inventory::id}, - {ObjectStore::FieldType::Double, "amount", PropertyNames::Inventory::amount} + {ObjectStore::FieldType::Int, "id", PropertyNames::Inventory::id }, + {ObjectStore::FieldType::Double, "amount", PropertyNames::Inventory::amount}, } }; template<> ObjectStore::JunctionTableDefinitions const JUNCTION_TABLES {}; @@ -324,21 +331,6 @@ namespace { /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Database field mappings for Misc /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - EnumStringMapping const MISC_TYPE_ENUM { - {"Spice", Misc::Type::Spice}, - {"Fining", Misc::Type::Fining}, - {"Water Agent", Misc::Type::Water_Agent}, - {"Herb", Misc::Type::Herb}, - {"Flavor", Misc::Type::Flavor}, - {"Other", Misc::Type::Other} - }; - EnumStringMapping const MISC_USE_ENUM { - {"Boil", Misc::Use::Boil}, - {"Mash", Misc::Use::Mash}, - {"Primary", Misc::Use::Primary}, - {"Secondary", Misc::Use::Secondary}, - {"Bottling", Misc::Use::Bottling} - }; template<> ObjectStore::TableDefinition const PRIMARY_TABLE { "misc", { @@ -348,13 +340,16 @@ namespace { {ObjectStore::FieldType::Bool, "display", PropertyNames::NamedEntity::display }, {ObjectStore::FieldType::String, "folder", PropertyNames::NamedEntity::folder }, {ObjectStore::FieldType::Int, "inventory_id", PropertyNames::NamedEntityWithInventory::inventoryId, nullptr, &PRIMARY_TABLE}, - {ObjectStore::FieldType::Enum, "mtype", PropertyNames::Misc::type, &MISC_TYPE_ENUM}, - {ObjectStore::FieldType::Enum, "use", PropertyNames::Misc::use, &MISC_USE_ENUM}, - {ObjectStore::FieldType::Double, "time", PropertyNames::Misc::time }, + {ObjectStore::FieldType::Enum, "mtype", PropertyNames::Misc::type, &Misc::typeStringMapping}, + {ObjectStore::FieldType::Enum, "use", PropertyNames::Misc::use, &Misc::useStringMapping}, + {ObjectStore::FieldType::Double, "time", PropertyNames::Misc::time_min }, {ObjectStore::FieldType::Double, "amount", PropertyNames::Misc::amount }, {ObjectStore::FieldType::Bool, "amount_is_weight", PropertyNames::Misc::amountIsWeight }, {ObjectStore::FieldType::String, "use_for", PropertyNames::Misc::useFor }, - {ObjectStore::FieldType::String, "notes", PropertyNames::Misc::notes } + {ObjectStore::FieldType::String, "notes", PropertyNames::Misc::notes }, + // ⮜⮜⮜ All below added for BeerJSON support ⮞⮞⮞ + {ObjectStore::FieldType::String, "producer", PropertyNames::Misc::producer }, + {ObjectStore::FieldType::String, "product_id", PropertyNames::Misc::productId }, } }; template<> ObjectStore::JunctionTableDefinitions const JUNCTION_TABLES { @@ -363,7 +358,7 @@ namespace { { {ObjectStore::FieldType::Int, "id" }, {ObjectStore::FieldType::Int, "child_id", PropertyNames::NamedEntity::key, nullptr, &PRIMARY_TABLE}, - {ObjectStore::FieldType::Int, "parent_id", PropertyNames::NamedEntity::parentKey, nullptr, &PRIMARY_TABLE} + {ObjectStore::FieldType::Int, "parent_id", PropertyNames::NamedEntity::parentKey, nullptr, &PRIMARY_TABLE}, }, ObjectStore::MAX_ONE_ENTRY } @@ -385,7 +380,7 @@ namespace { {ObjectStore::FieldType::Bool, "amount_is_weight", PropertyNames::Salt::amountIsWeight }, {ObjectStore::FieldType::Bool, "is_acid", PropertyNames::Salt::isAcid }, {ObjectStore::FieldType::Double, "percent_acid", PropertyNames::Salt::percentAcid }, - {ObjectStore::FieldType::Int, "stype", PropertyNames::Salt::type } // TODO: Really an Enum. Would be less fragile to store this as text than a number + {ObjectStore::FieldType::Int, "stype", PropertyNames::Salt::type }, // TODO: Really an Enum. Would be less fragile to store this as text than a number } }; // Salts don't have children @@ -395,12 +390,12 @@ namespace { // Database field mappings for Style /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// EnumStringMapping const STYLE_TYPE_ENUM { - {"Lager", Style::Type::Lager}, - {"Ale", Style::Type::Ale}, - {"Mead", Style::Type::Mead}, - {"Wheat", Style::Type::Wheat}, - {"Mixed", Style::Type::Mixed}, - {"Cider", Style::Type::Cider} + {Style::Type::Lager, "Lager"}, + {Style::Type::Ale , "Ale" }, + {Style::Type::Mead , "Mead" }, + {Style::Type::Wheat, "Wheat"}, + {Style::Type::Mixed, "Mixed"}, + {Style::Type::Cider, "Cider"}, }; template<> ObjectStore::TableDefinition const PRIMARY_TABLE